Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-05 06:12:53 +00:00
parent ee231234eb
commit 6d706d5dc0
31 changed files with 381 additions and 331 deletions

View File

@ -1,4 +1,5 @@
<!-- Audit Event documentation: See https://docs.gitlab.com/ee/administration/audit_events.html -->
<!-- Streaming Audit Event documentation: See https://docs.gitlab.com/ee/administration/audit_event_streaming.html -->
## Audit need
@ -8,6 +9,11 @@
<!-- Describe the audit event you are proposing should be added, including any details of what should be captured, how, and why. -->
### Streaming-only event or normal event?
<!-- Should this event be a streaming-only audit event or also logged to GitLab's database? Consider the
volume of data that will be generated by the event when answering this. -->
/label ~"Category:Audit Events"
/label ~"type::feature"
/label ~"group::compliance"

View File

@ -4,20 +4,6 @@ Layout/HashAlignment:
Exclude:
- 'ee/spec/lib/ee/gitlab/usage_data_spec.rb'
- 'spec/controllers/projects/merge_requests_controller_spec.rb'
- 'spec/lib/backup/gitaly_backup_spec.rb'
- 'spec/lib/banzai/filter/repository_link_filter_spec.rb'
- 'spec/lib/gitlab/asciidoc_spec.rb'
- 'spec/lib/gitlab/auth/ldap/auth_hash_spec.rb'
- 'spec/lib/gitlab/auth/ldap/config_spec.rb'
- 'spec/lib/gitlab/auth/ldap/person_spec.rb'
- 'spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb'
- 'spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb'
- 'spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb'
- 'spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb'
- 'spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb'
- 'spec/lib/gitlab/background_migration/encrypt_integration_properties_spec.rb'
- 'spec/lib/gitlab/ci/config/entry/port_spec.rb'
- 'spec/lib/gitlab/ci/config/entry/root_spec.rb'
- 'spec/lib/gitlab/ci/config/external/mapper_spec.rb'
- 'spec/lib/gitlab/ci/lint_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb'

View File

@ -11,7 +11,7 @@ gem 'responders', '~> 3.0'
gem 'sprockets', '~> 3.7.0'
gem 'view_component', '~> 2.69.0'
gem 'view_component', '~> 2.71.0'
# Default values for AR models
gem 'default_value_for', '~> 3.4.0'
@ -379,7 +379,7 @@ group :development, :test do
gem 'spring', '~> 2.1.0'
gem 'spring-commands-rspec', '~> 1.0.4'
gem 'gitlab-styles', '~> 7.1.0', require: false
gem 'gitlab-styles', '~> 8.0.0', require: false
gem 'haml_lint', '~> 0.40.0', require: false
gem 'bundler-audit', '~> 0.7.0.1', require: false

View File

@ -571,7 +571,7 @@ GEM
openid_connect (~> 1.2)
gitlab-sidekiq-fetcher (0.8.0)
sidekiq (~> 6.1)
gitlab-styles (7.1.0)
gitlab-styles (8.0.0)
rubocop (~> 0.91, >= 0.91.1)
rubocop-gitlab-security (~> 0.1.1)
rubocop-graphql (~> 0.10)
@ -992,7 +992,7 @@ GEM
rspec (>= 2.14)
term-ansicolor (~> 1.0)
parallel (1.22.1)
parser (3.1.2.0)
parser (3.1.2.1)
ast (~> 2.4.1)
parslet (1.8.2)
pastel (0.8.0)
@ -1199,11 +1199,11 @@ GEM
rubocop-ast (>= 0.6.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 2.0)
rubocop-ast (1.19.1)
rubocop-ast (1.21.0)
parser (>= 3.1.1.0)
rubocop-gitlab-security (0.1.1)
rubocop (>= 0.51)
rubocop-graphql (0.14.3)
rubocop-graphql (0.14.6)
rubocop (>= 0.87, < 2)
rubocop-performance (1.9.2)
rubocop (>= 0.90.0, < 2.0)
@ -1452,7 +1452,7 @@ GEM
activesupport (>= 3.0)
version_gem (1.0.0)
version_sorter (2.2.4)
view_component (2.69.0)
view_component (2.71.0)
activesupport (>= 5.0.0, < 8.0)
concurrent-ruby (~> 1.0)
method_source (~> 1.0)
@ -1599,7 +1599,7 @@ DEPENDENCIES
gitlab-net-dns (~> 0.9.1)
gitlab-omniauth-openid-connect (~> 0.9.0)
gitlab-sidekiq-fetcher (= 0.8.0)
gitlab-styles (~> 7.1.0)
gitlab-styles (~> 8.0.0)
gitlab_chronic_duration (~> 0.10.6.2)
gitlab_omniauth-ldap (~> 2.2.0)
gon (~> 6.4.0)
@ -1783,7 +1783,7 @@ DEPENDENCIES
valid_email (~> 0.1)
validates_hostname (~> 1.0.11)
version_sorter (~> 2.2.4)
view_component (~> 2.69.0)
view_component (~> 2.71.0)
vmstat (~> 2.3.0)
warning (~> 1.3.0)
webauthn (~> 2.3)

View File

@ -30,11 +30,6 @@ export default {
type: Boolean,
required: true,
},
hideOptions: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
@ -82,22 +77,37 @@ export default {
class="gl-display-flex discussions-counter"
>
<div
class="gl-display-flex gl-align-items-center gl-pl-4 gl-rounded-base gl-mr-3"
class="gl-display-flex gl-align-items-center gl-pl-4 gl-rounded-base gl-mr-3 gl-min-h-7"
:class="{
'gl-bg-orange-50': blocksMerge && !allResolved && !glFeatures.movedMrSidebar,
'gl-bg-gray-50': !blocksMerge || allResolved || glFeatures.movedMrSidebar,
'gl-pr-4': allResolved && glFeatures.movedMrSidebar,
'gl-pr-2': !allResolved && !glFeatures.movedMrSidebar,
'gl-min-h-7': glFeatures.movedMrSidebar,
'gl-bg-orange-50': blocksMerge && !allResolved,
'gl-bg-gray-50': !blocksMerge || allResolved,
'gl-pr-2': !allResolved,
}"
data-testid="discussions-counter-text"
>
<template v-if="allResolved">
{{ __('All threads resolved!') }}
<gl-dropdown
size="small"
category="tertiary"
right
toggle-class="btn-icon"
class="gl-pt-0! gl-px-2 gl-h-full gl-ml-2"
>
<template #button-content>
<gl-icon name="ellipsis_v" class="mr-0" />
</template>
<gl-dropdown-item
data-testid="toggle-all-discussions-btn"
@click="handleExpandDiscussions"
>
{{ toggleThreadsLabel }}
</gl-dropdown-item>
</gl-dropdown>
</template>
<template v-else>
{{ n__('%d unresolved thread', '%d unresolved threads', unresolvedDiscussionsCount) }}
<gl-button-group :class="{ 'gl-mr-2': hideOptions }" class="gl-ml-3">
<gl-button-group class="gl-ml-3">
<gl-button
v-gl-tooltip:discussionCounter.hover.bottom
:title="__('Go to previous unresolved thread')"
@ -123,7 +133,6 @@ export default {
@click="jumpNext"
/>
<gl-dropdown
v-if="glFeatures.movedMrSidebar && !hideOptions"
size="small"
category="tertiary"
right
@ -133,7 +142,10 @@ export default {
<template #button-content>
<gl-icon name="ellipsis_v" class="mr-0" />
</template>
<gl-dropdown-item @click="handleExpandDiscussions">
<gl-dropdown-item
data-testid="toggle-all-discussions-btn"
@click="handleExpandDiscussions"
>
{{ toggleThreadsLabel }}
</gl-dropdown-item>
<gl-dropdown-item
@ -146,24 +158,5 @@ export default {
</gl-button-group>
</template>
</div>
<gl-button-group v-if="!glFeatures.movedMrSidebar">
<gl-button
v-gl-tooltip
:title="toggleThreadsLabel"
:aria-label="toggleThreadsLabel"
class="toggle-all-discussions-btn"
:icon="allExpanded ? 'collapse' : 'expand'"
@click="handleExpandDiscussions"
/>
<gl-button
v-if="resolveAllDiscussionsIssuePath && !allResolved"
v-gl-tooltip
:href="resolveAllDiscussionsIssuePath"
:title="__('Create issue to resolve all threads')"
:aria-label="__('Create issue to resolve all threads')"
class="new-issue-for-discussion discussion-create-issue-btn"
icon="issue-new"
/>
</gl-button-group>
</div>
</template>

View File

@ -12,7 +12,7 @@ module Projects
private
def ensure_renderable
render_404 unless Feature.enabled?(:shimo_integration, project) && project.has_shimo? && project.shimo_integration&.render?
render_404 unless project.has_shimo? && project.shimo_integration&.render?
end
end
end

View File

@ -9,8 +9,6 @@ module Integrations
required: true
def render?
return false unless Feature.enabled?(:shimo_integration, project)
valid? && activated?
end

View File

@ -1565,9 +1565,7 @@ class Project < ApplicationRecord
end
def disabled_integrations
disabled_integrations = []
disabled_integrations << 'shimo' unless Feature.enabled?(:shimo_integration, self)
disabled_integrations
[]
end
def find_or_initialize_integration(name)

View File

@ -1,8 +0,0 @@
---
name: shimo_integration
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73129
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345356
milestone: '14.5'
type: development
group: group::integrations
default_enabled: false

View File

@ -691,6 +691,36 @@ Get Confluence integration settings for a project.
GET /projects/:id/integrations/confluence
```
## Shimo integration
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/343386) in GitLab 14.5 [with a flag](../administration/feature_flags.md) named `shimo_integration`. Disabled by default.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/343386) in GitLab 15.4.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/343386) in GitLab 15.4. [Feature flag `shimo_integration`](https://gitlab.com/gitlab-org/gitlab/-/issues/345356) removed.
Replaces the link to the internal wiki with a link to a Shimo Workspace.
### Create/Edit Shimo integration
Set Shimo integration for a project.
```plaintext
PUT /projects/:id/integrations/shimo
```
Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `external_wiki_url` | string | true | Shimo Workspace URL |
### Disable Shimo integration
Disable the Shimo integration for a project. Integration settings are preserved.
```plaintext
DELETE /projects/:id/integrations/shimo
```
## External wiki
Replaces the link to the internal wiki with a link to an external wiki.

View File

@ -325,6 +325,7 @@ deploy:
script:
- echo "//${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}">.npmrc
- npm publish
environment: production
```
See the

View File

@ -403,6 +403,7 @@ updated:
- dotnet nuget push "bin/Release/*.nupkg" --source gitlab
only:
- main
environment: production
```
1. Commit the changes and push it to your GitLab repository to trigger a new CI/CD build.

View File

@ -76,6 +76,7 @@ You can configure the following integrations.
| [Pumble](pumble.md) | Send event notifications to a Pumble channel. | **{dotted-circle}** No |
| Pushover | Get real-time notifications on your device. | **{dotted-circle}** No |
| [Redmine](redmine.md) | Use Redmine as the issue tracker. | **{dotted-circle}** No |
| [Shimo Workspace](shimo.md) | Use Shimo instead of the GitLab Wiki. | **{dotted-circle}** No |
| [Slack application](gitlab_slack_application.md) | Use Slack's official GitLab application. | **{dotted-circle}** No |
| [Slack notifications](slack.md) | Send notifications about project events to Slack. | **{dotted-circle}** No |
| [Slack slash commands](slack_slash_commands.md) | Enable slash commands in a workspace. | **{dotted-circle}** No |

View File

@ -0,0 +1,34 @@
---
stage: Ecosystem
group: Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Shimo Workspace integration **(FREE)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/343386) in GitLab 14.5 with a feature flag named `shimo_integration`. Disabled by default.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/345356) in GitLab 15.4.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/345356) in GitLab 15.4. [Feature flag `shimo_integration`](https://gitlab.com/gitlab-org/gitlab/-/issues/345356) removed.
[Shimo](https://shimo.im/) is a productivity suite that includes documents, spreadsheets, and slideshows in one interface. With this integration, you can use the Shimo Wiki directly within GitLab instead of the [GitLab group/project wiki](../wiki/index.md).
## Configure settings in GitLab
To enable the Shimo Workspace integration for your group or project:
1. On the top bar, select **Menu** and find your group or project.
1. On the left sidebar, select **Settings > Integrations**.
1. In **Add an integration**, select **Shimo Workspace**.
1. In **Enable integration**, ensure the **Active** checkbox is selected.
1. Provide the **Shimo Workspace URL** you want to link to your group or project (for example, `https://shimo.im/space/aBAYV6VvajUP873j`).
1. Select **Save changes**.
On the left sidebar, **Shimo** now appears instead of **Wiki**.
## View the Shimo Workspace
To view the Shimo Workspace from your group or project:
1. On the top bar, select **Menu** and find your group or project.
1. On the left sidebar, select **Shimo**.
1. On the **Shimo Workspace** page, select **Go to Shimo Workspace**.

View File

@ -51,8 +51,7 @@ module Sidebars
end
def third_party_wiki_menu
wiki_menu_list = [::Sidebars::Projects::Menus::ConfluenceMenu]
wiki_menu_list << ::Sidebars::Projects::Menus::ShimoMenu if Feature.enabled?(:shimo_integration, context.project)
wiki_menu_list = [::Sidebars::Projects::Menus::ConfluenceMenu, ::Sidebars::Projects::Menus::ShimoMenu]
wiki_menu_list.find { |wiki_menu| wiki_menu.new(context).render? }
end

View File

@ -30,8 +30,10 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
end
it 'shows a button to resolve all threads by creating a new issue' do
find('.discussions-counter .dropdown-toggle').click
within('.discussions-counter') do
expect(page).to have_selector resolve_all_discussions_link_selector( title: "Create issue to resolve all threads" )
expect(page).to have_link(_("Create issue to resolve all threads"), href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid))
end
end
@ -48,6 +50,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
context 'creating an issue for threads' do
before do
find('.discussions-counter .dropdown-toggle').click
find(resolve_all_discussions_link_selector).click
end

View File

@ -1,5 +1,5 @@
import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { GlDropdownItem } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import DiscussionCounter from '~/notes/components/discussion_counter.vue';
@ -45,7 +45,7 @@ describe('DiscussionCounter component', () => {
describe('has no discussions', () => {
it('does not render', () => {
wrapper = shallowMount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
wrapper = mount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
expect(wrapper.findComponent({ ref: 'discussionCounter' }).exists()).toBe(false);
});
@ -55,7 +55,7 @@ describe('DiscussionCounter component', () => {
it('does not render', () => {
store.commit(types.ADD_OR_UPDATE_DISCUSSIONS, [{ ...discussionMock, resolvable: false }]);
store.dispatch('updateResolvableDiscussionsCounts');
wrapper = shallowMount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
wrapper = mount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
expect(wrapper.findComponent({ ref: 'discussionCounter' }).exists()).toBe(false);
});
@ -75,7 +75,7 @@ describe('DiscussionCounter component', () => {
it('renders', () => {
updateStore();
wrapper = shallowMount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
wrapper = mount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
expect(wrapper.findComponent({ ref: 'discussionCounter' }).exists()).toBe(true);
});
@ -89,7 +89,7 @@ describe('DiscussionCounter component', () => {
({ blocksMerge, color }) => {
updateStore();
store.state.unresolvedDiscussionsCount = 1;
wrapper = shallowMount(DiscussionCounter, { store, propsData: { blocksMerge } });
wrapper = mount(DiscussionCounter, { store, propsData: { blocksMerge } });
expect(wrapper.find('[data-testid="discussions-counter-text"]').classes()).toContain(color);
},
@ -97,60 +97,58 @@ describe('DiscussionCounter component', () => {
it.each`
title | resolved | groupLength
${'not allResolved'} | ${false} | ${4}
${'not allResolved'} | ${false} | ${2}
${'allResolved'} | ${true} | ${1}
`('renders correctly if $title', ({ resolved, groupLength }) => {
`('renders correctly if $title', async ({ resolved, groupLength }) => {
updateStore({ resolvable: true, resolved });
wrapper = shallowMount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
wrapper = mount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
await wrapper.find('.dropdown-toggle').trigger('click');
expect(wrapper.findAllComponents(GlButton)).toHaveLength(groupLength);
expect(wrapper.findAllComponents(GlDropdownItem)).toHaveLength(groupLength);
});
});
describe('toggle all threads button', () => {
let toggleAllButton;
const updateStoreWithExpanded = (expanded) => {
const updateStoreWithExpanded = async (expanded) => {
const discussion = { ...discussionMock, expanded };
store.commit(types.ADD_OR_UPDATE_DISCUSSIONS, [discussion]);
store.dispatch('updateResolvableDiscussionsCounts');
wrapper = shallowMount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
toggleAllButton = wrapper.find('.toggle-all-discussions-btn');
wrapper = mount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
await wrapper.find('.dropdown-toggle').trigger('click');
toggleAllButton = wrapper.find('[data-testid="toggle-all-discussions-btn"]');
};
afterEach(() => wrapper.destroy());
it('calls button handler when clicked', () => {
updateStoreWithExpanded(true);
it('calls button handler when clicked', async () => {
await updateStoreWithExpanded(true);
toggleAllButton.vm.$emit('click');
toggleAllButton.trigger('click');
expect(setExpandDiscussionsFn).toHaveBeenCalledTimes(1);
});
it('collapses all discussions if expanded', async () => {
updateStoreWithExpanded(true);
await updateStoreWithExpanded(true);
expect(wrapper.vm.allExpanded).toBe(true);
expect(toggleAllButton.props('icon')).toBe('collapse');
toggleAllButton.vm.$emit('click');
toggleAllButton.trigger('click');
await nextTick();
expect(wrapper.vm.allExpanded).toBe(false);
expect(toggleAllButton.props('icon')).toBe('expand');
});
it('expands all discussions if collapsed', async () => {
updateStoreWithExpanded(false);
await updateStoreWithExpanded(false);
expect(wrapper.vm.allExpanded).toBe(false);
expect(toggleAllButton.props('icon')).toBe('expand');
toggleAllButton.vm.$emit('click');
toggleAllButton.trigger('click');
await nextTick();
expect(wrapper.vm.allExpanded).toBe(true);
expect(toggleAllButton.props('icon')).toBe('collapse');
});
});
});

View File

@ -17,7 +17,7 @@ RSpec.describe Backup::GitalyBackup do
let(:expected_env) do
{
'SSL_CERT_FILE' => Gitlab::X509::Certificate.default_cert_file,
'SSL_CERT_DIR' => Gitlab::X509::Certificate.default_cert_dir
'SSL_CERT_DIR' => Gitlab::X509::Certificate.default_cert_dir
}.merge(ENV)
end
@ -121,7 +121,7 @@ RSpec.describe Backup::GitalyBackup do
let(:ssl_env) do
{
'SSL_CERT_FILE' => '/some/cert/file',
'SSL_CERT_DIR' => '/some/cert'
'SSL_CERT_DIR' => '/some/cert'
}
end

View File

@ -8,14 +8,14 @@ RSpec.describe Banzai::Filter::RepositoryLinkFilter do
def filter(doc, contexts = {})
contexts.reverse_merge!({
commit: commit,
project: project,
current_user: user,
group: group,
wiki: wiki,
ref: ref,
commit: commit,
project: project,
current_user: user,
group: group,
wiki: wiki,
ref: ref,
requested_path: requested_path,
only_path: only_path
only_path: only_path
})
described_class.call(doc, contexts)

View File

@ -638,9 +638,9 @@ module Gitlab
context 'with project' do
let(:context) do
{
commit: commit,
project: project,
ref: ref,
commit: commit,
project: project,
ref: ref,
requested_path: requested_path
}
end

View File

@ -20,17 +20,17 @@ RSpec.describe Gitlab::Auth::Ldap::AuthHash do
let(:info) do
{
name: 'Smith, J.',
email: 'johnsmith@example.com',
name: 'Smith, J.',
email: 'johnsmith@example.com',
nickname: '123456'
}
end
let(:raw_info) do
{
uid: ['123456'],
email: ['johnsmith@example.com'],
cn: ['Smith, J.'],
uid: ['123456'],
email: ['johnsmith@example.com'],
cn: ['Smith, J.'],
fullName: ['John Smith']
}
end
@ -52,8 +52,8 @@ RSpec.describe Gitlab::Auth::Ldap::AuthHash do
let(:attributes) do
{
'username' => %w(mail email),
'name' => 'fullName'
'username' => %w(mail email),
'name' => 'fullName'
}
end

View File

@ -112,8 +112,8 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'constructs basic options' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 386,
'host' => 'ldap.example.com',
'port' => 386,
'encryption' => 'plain'
}
)
@ -129,16 +129,16 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'includes failover hosts when set' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'hosts' => [
'host' => 'ldap.example.com',
'port' => 686,
'hosts' => [
['ldap1.example.com', 636],
['ldap2.example.com', 636]
],
'encryption' => 'simple_tls',
'encryption' => 'simple_tls',
'verify_certificates' => true,
'bind_dn' => 'uid=admin,dc=example,dc=com',
'password' => 'super_secret'
'bind_dn' => 'uid=admin,dc=example,dc=com',
'password' => 'super_secret'
}
)
@ -158,12 +158,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'includes authentication options when auth is configured' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'verify_certificates' => true,
'bind_dn' => 'uid=admin,dc=example,dc=com',
'password' => 'super_secret'
'bind_dn' => 'uid=admin,dc=example,dc=com',
'password' => 'super_secret'
}
)
@ -179,9 +179,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'sets encryption method to simple_tls when configured as simple_tls' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls'
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls'
}
)
@ -191,9 +191,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'sets encryption method to start_tls when configured as start_tls' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'start_tls'
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'start_tls'
}
)
@ -203,12 +203,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'transforms SSL cert and key to OpenSSL objects' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'start_tls',
'tls_options' => {
'cert' => raw_cert,
'key' => raw_key
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'start_tls',
'tls_options' => {
'cert' => raw_cert,
'key' => raw_key
}
}
)
@ -221,12 +221,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
allow(Gitlab::AppLogger).to receive(:error)
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'start_tls',
'tls_options' => {
'cert' => 'invalid cert',
'key' => 'invalid_key'
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'start_tls',
'tls_options' => {
'cert' => 'invalid cert',
'key' => 'invalid_key'
}
}
)
@ -240,9 +240,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'sets tls_options to OpenSSL defaults' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'verify_certificates' => true
}
)
@ -255,9 +255,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'sets verify_mode to OpenSSL VERIFY_NONE' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'verify_certificates' => false
}
)
@ -274,11 +274,11 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'passes it through in tls_options' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'tls_options' => {
'ca_file' => '/etc/ca.pem'
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'tls_options' => {
'ca_file' => '/etc/ca.pem'
}
}
)
@ -291,11 +291,11 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'does not add the ca_file key to tls_options' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'tls_options' => {
'ca_file' => ' '
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'tls_options' => {
'ca_file' => ' '
}
}
)
@ -308,11 +308,11 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'passes it through in tls_options' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'tls_options' => {
'ssl_version' => 'TLSv1_2'
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'tls_options' => {
'ssl_version' => 'TLSv1_2'
}
}
)
@ -325,11 +325,11 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'does not add the ssl_version key to tls_options' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'tls_options' => {
'ssl_version' => ' '
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'tls_options' => {
'ssl_version' => ' '
}
}
)
@ -343,11 +343,11 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'constructs basic options' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 386,
'base' => 'ou=users,dc=example,dc=com',
'host' => 'ldap.example.com',
'port' => 386,
'base' => 'ou=users,dc=example,dc=com',
'encryption' => 'plain',
'uid' => 'uid'
'uid' => 'uid'
}
)
@ -364,10 +364,10 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'includes authentication options when auth is configured' do
stub_ldap_config(
options: {
'uid' => 'sAMAccountName',
'uid' => 'sAMAccountName',
'user_filter' => '(memberOf=cn=group1,ou=groups,dc=example,dc=com)',
'bind_dn' => 'uid=admin,dc=example,dc=com',
'password' => 'super_secret'
'bind_dn' => 'uid=admin,dc=example,dc=com',
'password' => 'super_secret'
}
)
@ -381,12 +381,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'transforms SSL cert and key to OpenSSL objects' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'start_tls',
'tls_options' => {
'cert' => raw_cert,
'key' => raw_key
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'start_tls',
'tls_options' => {
'cert' => raw_cert,
'key' => raw_key
}
}
)
@ -399,9 +399,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'specifies disable_verify_certificates as false' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'verify_certificates' => true
}
)
@ -414,9 +414,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'specifies disable_verify_certificates as true' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'verify_certificates' => false
}
)
@ -429,12 +429,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'passes it through' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'verify_certificates' => true,
'tls_options' => {
'ca_file' => '/etc/ca.pem'
'tls_options' => {
'ca_file' => '/etc/ca.pem'
}
}
)
@ -447,12 +447,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'does not include the ca_file option' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'verify_certificates' => true,
'tls_options' => {
'ca_file' => ' '
'tls_options' => {
'ca_file' => ' '
}
}
)
@ -465,12 +465,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'passes it through' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'verify_certificates' => true,
'tls_options' => {
'ssl_version' => 'TLSv1_2'
'tls_options' => {
'ssl_version' => 'TLSv1_2'
}
}
)
@ -483,12 +483,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'does not include the ssl_version option' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
'verify_certificates' => true,
'tls_options' => {
'ssl_version' => ' '
'tls_options' => {
'ssl_version' => ' '
}
}
)
@ -503,7 +503,7 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'is true when password is set' do
stub_ldap_config(
options: {
'bind_dn' => 'uid=admin,dc=example,dc=com',
'bind_dn' => 'uid=admin,dc=example,dc=com',
'password' => 'super_secret'
}
)
@ -514,7 +514,7 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'is true when bind_dn is set and password is empty' do
stub_ldap_config(
options: {
'bind_dn' => 'uid=admin,dc=example,dc=com',
'bind_dn' => 'uid=admin,dc=example,dc=com',
'password' => ''
}
)
@ -539,15 +539,15 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
options: {
'attributes' => {
'username' => %w(sAMAccountName),
'email' => %w(userPrincipalName)
'email' => %w(userPrincipalName)
}
}
)
expect(config.attributes).to include({
'username' => %w(sAMAccountName),
'email' => %w(userPrincipalName),
'name' => 'cn'
'email' => %w(userPrincipalName),
'name' => 'cn'
})
end
end

View File

@ -10,10 +10,10 @@ RSpec.describe Gitlab::Auth::Ldap::Person do
before do
stub_ldap_config(
options: {
'uid' => 'uid',
'uid' => 'uid',
'attributes' => {
'name' => 'cn',
'email' => %w(mail email userPrincipalName),
'name' => 'cn',
'email' => %w(mail email userPrincipalName),
'username' => username_attribute
}
}
@ -53,10 +53,10 @@ RSpec.describe Gitlab::Auth::Ldap::Person do
it 'returns a compact and unique array' do
stub_ldap_config(
options: {
'uid' => nil,
'uid' => nil,
'attributes' => {
'name' => 'cn',
'email' => 'mail',
'name' => 'cn',
'email' => 'mail',
'username' => %w(uid mail),
'first_name' => ''
}

View File

@ -40,12 +40,12 @@ RSpec.describe Gitlab::Auth::OAuth::AuthHash do
let(:info_hash) do
{
email: email_ascii,
email: email_ascii,
first_name: first_name_ascii,
last_name: last_name_ascii,
name: name_ascii,
nickname: nickname_ascii,
uid: uid_ascii,
last_name: last_name_ascii,
name: name_ascii,
nickname: nickname_ascii,
uid: uid_ascii,
address: {
locality: 'some locality',
country: 'some country'

View File

@ -49,10 +49,10 @@ RSpec.describe Gitlab::Auth::Otp::Strategies::FortiTokenCloud do
stub_request(:post, otp_verification_url)
.with(body: JSON(otp_verification_request_body),
headers: {
'Content-Type' => 'application/json',
'Authorization' => "Bearer #{access_token}"
})
headers: {
'Content-Type' => 'application/json',
'Authorization' => "Bearer #{access_token}"
})
.to_return(status: otp_verification_response_status, body: '', headers: {})
end

View File

@ -14,10 +14,10 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillImportedIssueSearchData,
table(:projects)
.create!(
namespace_id: namespace.id,
creator_id: user.id,
name: 'projecty',
path: 'path',
project_namespace_id: namespace.id)
creator_id: user.id,
name: 'projecty',
path: 'path',
project_namespace_id: namespace.id)
end
let!(:issue) do

View File

@ -9,25 +9,35 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillIntegrationsEnableSslVerific
before do
integrations.create!(id: 1, type_new: 'Integrations::Bamboo') # unaffected integration
integrations.create!(id: 2, type_new: 'Integrations::DroneCi') # no properties
integrations.create!(id: 3, type_new: 'Integrations::DroneCi',
integrations.create!(
id: 3, type_new: 'Integrations::DroneCi',
properties: {}) # no URL
integrations.create!(id: 4, type_new: 'Integrations::DroneCi',
integrations.create!(
id: 4, type_new: 'Integrations::DroneCi',
properties: { 'drone_url' => '' }) # blank URL
integrations.create!(id: 5, type_new: 'Integrations::DroneCi',
integrations.create!(
id: 5, type_new: 'Integrations::DroneCi',
properties: { 'drone_url' => 'https://example.com:foo' }) # invalid URL
integrations.create!(id: 6, type_new: 'Integrations::DroneCi',
integrations.create!(
id: 6, type_new: 'Integrations::DroneCi',
properties: { 'drone_url' => 'https://example.com' }) # unknown URL
integrations.create!(id: 7, type_new: 'Integrations::DroneCi',
integrations.create!(
id: 7, type_new: 'Integrations::DroneCi',
properties: { 'drone_url' => 'http://cloud.drone.io' }) # no HTTPS
integrations.create!(id: 8, type_new: 'Integrations::DroneCi',
integrations.create!(
id: 8, type_new: 'Integrations::DroneCi',
properties: { 'drone_url' => 'https://cloud.drone.io' }) # known URL
integrations.create!(id: 9, type_new: 'Integrations::Teamcity',
integrations.create!(
id: 9, type_new: 'Integrations::Teamcity',
properties: { 'teamcity_url' => 'https://example.com' }) # unknown URL
integrations.create!(id: 10, type_new: 'Integrations::Teamcity',
integrations.create!(
id: 10, type_new: 'Integrations::Teamcity',
properties: { 'teamcity_url' => 'https://foo.bar.teamcity.com' }) # unknown URL
integrations.create!(id: 11, type_new: 'Integrations::Teamcity',
integrations.create!(
id: 11, type_new: 'Integrations::Teamcity',
properties: { 'teamcity_url' => 'https://teamcity.com' }) # unknown URL
integrations.create!(id: 12, type_new: 'Integrations::Teamcity',
integrations.create!(
id: 12, type_new: 'Integrations::Teamcity',
properties: { 'teamcity_url' => 'https://customer.teamcity.com' }) # known URL
end

View File

@ -14,23 +14,23 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillSnippetRepositories, :migrat
let!(:user) do
users.create!(id: 1,
email: 'user@example.com',
projects_limit: 10,
username: 'test',
name: user_name,
state: user_state,
last_activity_on: 1.minute.ago,
user_type: user_type,
confirmed_at: 1.day.ago)
email: 'user@example.com',
projects_limit: 10,
username: 'test',
name: user_name,
state: user_state,
last_activity_on: 1.minute.ago,
user_type: user_type,
confirmed_at: 1.day.ago)
end
let!(:migration_bot) do
users.create!(id: 100,
email: "noreply+gitlab-migration-bot%s@#{Settings.gitlab.host}",
user_type: HasUserType::USER_TYPES[:migration_bot],
name: 'GitLab Migration Bot',
projects_limit: 10,
username: 'bot')
email: "noreply+gitlab-migration-bot%s@#{Settings.gitlab.host}",
user_type: HasUserType::USER_TYPES[:migration_bot],
name: 'GitLab Migration Bot',
projects_limit: 10,
username: 'bot')
end
let!(:snippet_with_repo) { snippets.create!(id: 1, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
@ -260,14 +260,14 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillSnippetRepositories, :migrat
let(:user_name) { '.' }
let!(:other_user) do
users.create!(id: 2,
email: 'user2@example.com',
projects_limit: 10,
username: 'test2',
name: 'Test2',
state: user_state,
last_activity_on: 1.minute.ago,
user_type: user_type,
confirmed_at: 1.day.ago)
email: 'user2@example.com',
projects_limit: 10,
username: 'test2',
name: 'Test2',
state: user_state,
last_activity_on: 1.minute.ago,
user_type: user_type,
confirmed_at: 1.day.ago)
end
let!(:invalid_snippet) { snippets.create!(id: 4, type: 'PersonalSnippet', author_id: user.id, file_name: '.', content: content) }

View File

@ -40,10 +40,10 @@ RSpec.describe Gitlab::BackgroundMigration::EncryptIntegrationProperties, schema
expect(integrations.count).to eq(4)
expect(props).to match(
no_properties.id => both(be_nil),
no_properties.id => both(be_nil),
with_plaintext_1.id => both(eq some_props(1)),
with_plaintext_2.id => both(eq some_props(2)),
with_encrypted.id => match([be_nil, eq(some_props(3))])
with_encrypted.id => match([be_nil, eq(some_props(3))])
)
end

View File

@ -48,7 +48,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Port do
let(:config) do
{ number: 80,
protocol: 'http',
name: 'foobar' }
name: 'foobar' }
end
describe '#valid?' do

View File

@ -117,49 +117,49 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
expect(root.jobs_value.keys).to eq([:rspec, :spinach, :release])
expect(root.jobs_value[:rspec]).to eq(
{ name: :rspec,
script: %w[rspec ls],
before_script: %w(ls pwd),
image: { name: 'image:1.0' },
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
job_variables: {},
root_variables_inheritance: true,
ignore: false,
after_script: ['make clean'],
only: { refs: %w[branches tags] },
scheduling_type: :stage }
script: %w[rspec ls],
before_script: %w(ls pwd),
image: { name: 'image:1.0' },
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
job_variables: {},
root_variables_inheritance: true,
ignore: false,
after_script: ['make clean'],
only: { refs: %w[branches tags] },
scheduling_type: :stage }
)
expect(root.jobs_value[:spinach]).to eq(
{ name: :spinach,
before_script: [],
script: %w[spinach],
image: { name: 'image:1.0' },
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
job_variables: {},
root_variables_inheritance: true,
ignore: false,
after_script: ['make clean'],
only: { refs: %w[branches tags] },
scheduling_type: :stage }
before_script: [],
script: %w[spinach],
image: { name: 'image:1.0' },
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
job_variables: {},
root_variables_inheritance: true,
ignore: false,
after_script: ['make clean'],
only: { refs: %w[branches tags] },
scheduling_type: :stage }
)
expect(root.jobs_value[:release]).to eq(
{ name: :release,
stage: 'release',
before_script: [],
script: ["make changelog | tee release_changelog.txt"],
release: { name: "Release $CI_TAG_NAME", tag_name: 'v0.06', description: "./release_changelog.txt" },
image: { name: "image:1.0" },
services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }],
cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success' }],
only: { refs: %w(branches tags) },
job_variables: { 'VAR' => 'job' },
root_variables_inheritance: true,
after_script: [],
ignore: false,
scheduling_type: :stage }
stage: 'release',
before_script: [],
script: ["make changelog | tee release_changelog.txt"],
release: { name: "Release $CI_TAG_NAME", tag_name: 'v0.06', description: "./release_changelog.txt" },
image: { name: "image:1.0" },
services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }],
cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success' }],
only: { refs: %w(branches tags) },
job_variables: { 'VAR' => 'job' },
root_variables_inheritance: true,
after_script: [],
ignore: false,
scheduling_type: :stage }
)
end
end
@ -196,31 +196,31 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
it 'returns jobs configuration' do
expect(root.jobs_value).to eq(
rspec: { name: :rspec,
script: %w[rspec ls],
before_script: %w(ls pwd),
image: { name: 'image:1.0' },
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
job_variables: {},
root_variables_inheritance: true,
ignore: false,
after_script: ['make clean'],
only: { refs: %w[branches tags] },
scheduling_type: :stage },
script: %w[rspec ls],
before_script: %w(ls pwd),
image: { name: 'image:1.0' },
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
job_variables: {},
root_variables_inheritance: true,
ignore: false,
after_script: ['make clean'],
only: { refs: %w[branches tags] },
scheduling_type: :stage },
spinach: { name: :spinach,
before_script: [],
script: %w[spinach],
image: { name: 'image:1.0' },
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
job_variables: { 'VAR' => 'job' },
root_variables_inheritance: true,
ignore: false,
after_script: ['make clean'],
only: { refs: %w[branches tags] },
scheduling_type: :stage }
before_script: [],
script: %w[spinach],
image: { name: 'image:1.0' },
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
job_variables: { 'VAR' => 'job' },
root_variables_inheritance: true,
ignore: false,
after_script: ['make clean'],
only: { refs: %w[branches tags] },
scheduling_type: :stage }
)
end
end