提交 8e812185 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 2f47b6d8
...@@ -75,7 +75,11 @@ export default { ...@@ -75,7 +75,11 @@ export default {
name="eye-slash" name="eye-slash"
class="suggestion-help-hover mr-1 suggestion-confidential" class="suggestion-help-hover mr-1 suggestion-confidential"
/> />
<gl-link :href="suggestion.webUrl" target="_blank" class="suggestion bold str-truncated-100"> <gl-link
:href="suggestion.webUrl"
target="_blank"
class="suggestion bold str-truncated-100 gl-text-gray-900!"
>
{{ suggestion.title }} {{ suggestion.title }}
</gl-link> </gl-link>
</div> </div>
......
...@@ -257,15 +257,6 @@ export default { ...@@ -257,15 +257,6 @@ export default {
{{ __('Copy link') }} {{ __('Copy link') }}
</button> </button>
</li> </li>
<li v-if="canEdit">
<button
class="btn btn-transparent js-note-delete js-note-delete"
type="button"
@click.prevent="onDelete"
>
<span class="text-danger">{{ __('Delete comment') }}</span>
</button>
</li>
<li v-if="isIssue"> <li v-if="isIssue">
<button <button
class="btn-default btn-transparent" class="btn-default btn-transparent"
...@@ -276,6 +267,15 @@ export default { ...@@ -276,6 +267,15 @@ export default {
{{ displayAssignUserText }} {{ displayAssignUserText }}
</button> </button>
</li> </li>
<li v-if="canEdit">
<button
class="btn btn-transparent js-note-delete js-note-delete"
type="button"
@click.prevent="onDelete"
>
<span class="text-danger">{{ __('Delete comment') }}</span>
</button>
</li>
</ul> </ul>
</div> </div>
</div> </div>
......
...@@ -18,17 +18,21 @@ export default class PersistentUserCallout { ...@@ -18,17 +18,21 @@ export default class PersistentUserCallout {
init() { init() {
const closeButton = this.container.querySelector('.js-close'); const closeButton = this.container.querySelector('.js-close');
const followLink = this.container.querySelector('.js-follow-link');
if (!closeButton) { if (closeButton) {
return; this.handleCloseButtonCallout(closeButton);
} else if (followLink) {
this.handleFollowLinkCallout(followLink);
} }
}
handleCloseButtonCallout(closeButton) {
closeButton.addEventListener('click', event => this.dismiss(event)); closeButton.addEventListener('click', event => this.dismiss(event));
if (this.deferLinks) { if (this.deferLinks) {
this.container.addEventListener('click', event => { this.container.addEventListener('click', event => {
const isDeferredLink = event.target.classList.contains(DEFERRED_LINK_CLASS); const isDeferredLink = event.target.classList.contains(DEFERRED_LINK_CLASS);
if (isDeferredLink) { if (isDeferredLink) {
const { href, target } = event.target; const { href, target } = event.target;
...@@ -38,6 +42,10 @@ export default class PersistentUserCallout { ...@@ -38,6 +42,10 @@ export default class PersistentUserCallout {
} }
} }
handleFollowLinkCallout(followLink) {
followLink.addEventListener('click', event => this.registerCalloutWithLink(event));
}
dismiss(event, deferredLinkOptions = null) { dismiss(event, deferredLinkOptions = null) {
event.preventDefault(); event.preventDefault();
...@@ -58,6 +66,27 @@ export default class PersistentUserCallout { ...@@ -58,6 +66,27 @@ export default class PersistentUserCallout {
}); });
} }
registerCalloutWithLink(event) {
event.preventDefault();
const { href } = event.currentTarget;
axios
.post(this.dismissEndpoint, {
feature_name: this.featureId,
})
.then(() => {
window.location.assign(href);
})
.catch(() => {
Flash(
__(
'An error occurred while acknowledging the notification. Refresh the page and try again.',
),
);
});
}
static factory(container, options) { static factory(container, options) {
if (!container) { if (!container) {
return undefined; return undefined;
......
...@@ -4,6 +4,7 @@ const PERSISTENT_USER_CALLOUTS = [ ...@@ -4,6 +4,7 @@ const PERSISTENT_USER_CALLOUTS = [
'.js-recovery-settings-callout', '.js-recovery-settings-callout',
'.js-users-over-license-callout', '.js-users-over-license-callout',
'.js-admin-licensed-user-count-threshold', '.js-admin-licensed-user-count-threshold',
'.js-buy-pipeline-minutes-notification-callout',
]; ];
const initCallouts = () => { const initCallouts = () => {
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
font-size: 13px; font-size: 13px;
word-break: break-all; word-break: break-all;
word-wrap: break-word; word-wrap: break-word;
color: $gl-text-color-inverted; color: color-yiq($builds-trace-bg);
border-radius: $border-radius-small; border-radius: $border-radius-small;
min-height: 42px; min-height: 42px;
background-color: $builds-trace-bg; background-color: $builds-trace-bg;
......
...@@ -969,10 +969,6 @@ ...@@ -969,10 +969,6 @@
vertical-align: sub; vertical-align: sub;
} }
.suggestion-item a {
color: initial;
}
.suggestion-confidential { .suggestion-confidential {
color: $orange-600; color: $orange-600;
} }
......
...@@ -115,6 +115,9 @@ $secondary: $gray-600; ...@@ -115,6 +115,9 @@ $secondary: $gray-600;
$issues-today-bg: #333838; $issues-today-bg: #333838;
$issues-today-border: #333a40; $issues-today-border: #333a40;
$yiq-text-dark: $gray-50;
$yiq-text-light: $gray-950;
.gl-label { .gl-label {
filter: brightness(0.9) contrast(1.1); filter: brightness(0.9) contrast(1.1);
} }
......
...@@ -19,7 +19,11 @@ module WikiActions ...@@ -19,7 +19,11 @@ module WikiActions
end end
before_action only: [:edit, :update], unless: :valid_encoding? do before_action only: [:edit, :update], unless: :valid_encoding? do
redirect_to wiki_page_path(wiki, page) if params[:id].present?
redirect_to wiki_page_path(wiki, page || params[:id])
else
redirect_to wiki_path(wiki)
end
end end
end end
......
...@@ -226,6 +226,11 @@ class Environment < ApplicationRecord ...@@ -226,6 +226,11 @@ class Environment < ApplicationRecord
available? && stop_action.present? available? && stop_action.present?
end end
def cancel_deployment_jobs!
jobs = all_deployments.active.with_deployable
jobs.each { |deployment| deployment.deployable.cancel! }
end
def stop_with_action!(current_user) def stop_with_action!(current_user)
return unless available? return unless available?
......
...@@ -93,7 +93,7 @@ class Iteration < ApplicationRecord ...@@ -93,7 +93,7 @@ class Iteration < ApplicationRecord
# ensure dates do not overlap with other Iterations in the same group/project # ensure dates do not overlap with other Iterations in the same group/project
def dates_do_not_overlap def dates_do_not_overlap
return unless resource_parent.iterations.within_timeframe(start_date, due_date).exists? return unless resource_parent.iterations.where.not(id: self.id).within_timeframe(start_date, due_date).exists?
errors.add(:base, s_("Iteration|Dates cannot overlap with other existing Iterations")) errors.add(:base, s_("Iteration|Dates cannot overlap with other existing Iterations"))
end end
......
...@@ -29,6 +29,11 @@ module MergeRequests ...@@ -29,6 +29,11 @@ module MergeRequests
.execute_for_merge_request(merge_request) .execute_for_merge_request(merge_request)
end end
def cancel_review_app_jobs!(merge_request)
environments = merge_request.environments.in_review_folder.available
environments.each { |environment| environment.cancel_deployment_jobs! }
end
def source_project def source_project
@source_project ||= merge_request.source_project @source_project ||= merge_request.source_project
end end
......
...@@ -18,6 +18,7 @@ module MergeRequests ...@@ -18,6 +18,7 @@ module MergeRequests
invalidate_cache_counts(merge_request, users: merge_request.assignees) invalidate_cache_counts(merge_request, users: merge_request.assignees)
merge_request.update_project_counter_caches merge_request.update_project_counter_caches
delete_non_latest_diffs(merge_request) delete_non_latest_diffs(merge_request)
cancel_review_app_jobs!(merge_request)
cleanup_environments(merge_request) cleanup_environments(merge_request)
end end
......
---
title: Cancel review app deployment when MR is merged
merge_request: 34960
author:
type: fixed
---
title: Move 'Delete comment' button to bottom of 'More actions' list
merge_request: 35237
author:
type: fixed
---
title: Fix issue suggestion text color on dark mode
merge_request: 34899
author:
type: fixed
---
title: Fix job log text color in dark mode
merge_request: 35387
author:
type: fixed
---
title: Redirect wiki edit actions for missing pages
merge_request: 35350
author:
type: fixed
...@@ -21,7 +21,7 @@ end ...@@ -21,7 +21,7 @@ end
scope path: '-' do scope path: '-' do
get '/snippets/:snippet_id/raw/:ref/*path', get '/snippets/:snippet_id/raw/:ref/*path',
to: 'snippets/blobs#raw', to: 'snippets/blobs#raw',
as: :snippet_raw, as: :snippet_blob_raw,
format: false, format: false,
constraints: { snippet_id: /\d+/ } constraints: { snippet_id: /\d+/ }
end end
......
...@@ -24,6 +24,7 @@ exceptions: ...@@ -24,6 +24,7 @@ exceptions:
- CSV - CSV
- DNS - DNS
- EKS - EKS
- FAQ
- GDK - GDK
- GET - GET
- GNU - GNU
...@@ -34,11 +35,14 @@ exceptions: ...@@ -34,11 +35,14 @@ exceptions:
- HTTPS - HTTPS
- IAM - IAM
- IDE - IDE
- IRC
- IBM
- JSON - JSON
- LDAP - LDAP
- LDAPS - LDAPS
- LESS - LESS
- LFS - LFS
- LRU
- NFS - NFS
- NGINX - NGINX
- NOTE - NOTE
...@@ -48,6 +52,7 @@ exceptions: ...@@ -48,6 +52,7 @@ exceptions:
- POST - POST
- PUT - PUT
- RPC - RPC
- RAM
- RSA - RSA
- RSS - RSS
- SAML - SAML
...@@ -58,11 +63,13 @@ exceptions: ...@@ -58,11 +63,13 @@ exceptions:
- SSH - SSH
- SSL - SSL
- SSO - SSO
- TCP
- TIP - TIP
- TLS - TLS
- TODO - TODO
- TOML - TOML
- UNIX - UNIX
- USB
- URI - URI
- URL - URL
- VPC - VPC
......
...@@ -248,6 +248,7 @@ memoization ...@@ -248,6 +248,7 @@ memoization
memoize memoize
memoized memoized
memoizing memoizing
Memorystore
mergeable mergeable
Microsoft Microsoft
middleware middleware
...@@ -335,6 +336,7 @@ Puma ...@@ -335,6 +336,7 @@ Puma
Python Python
Qualys Qualys
Rackspace Rackspace
Raspbian
reachability reachability
rebase rebase
rebased rebased
...@@ -422,6 +424,7 @@ storages ...@@ -422,6 +424,7 @@ storages
strace strace
strikethrough strikethrough
strikethroughs strikethroughs
stunnel
subpath subpath
subfolder subfolder
subfolders subfolders
......
...@@ -7944,6 +7944,7 @@ type Mutation { ...@@ -7944,6 +7944,7 @@ type Mutation {
""" """
updateImageDiffNote(input: UpdateImageDiffNoteInput!): UpdateImageDiffNotePayload updateImageDiffNote(input: UpdateImageDiffNoteInput!): UpdateImageDiffNotePayload
updateIssue(input: UpdateIssueInput!): UpdateIssuePayload updateIssue(input: UpdateIssueInput!): UpdateIssuePayload
updateIteration(input: UpdateIterationInput!): UpdateIterationPayload
""" """
Updates a Note. If the body of the Note contains only quick actions, the Note Updates a Note. If the body of the Note contains only quick actions, the Note
...@@ -13257,6 +13258,66 @@ type UpdateIssuePayload { ...@@ -13257,6 +13258,66 @@ type UpdateIssuePayload {
issue: Issue issue: Issue
} }
"""
Autogenerated input type of UpdateIteration
"""
input UpdateIterationInput {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
The description of the iteration
"""
description: String
"""
The end date of the iteration
"""
dueDate: String
"""
The group of the iteration
"""
groupPath: ID!
"""
The id of the iteration
"""
id: ID!
"""
The start date of the iteration
"""
startDate: String
"""
The title of the iteration
"""
title: String
}
"""
Autogenerated return type of UpdateIteration
"""
type UpdateIterationPayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Errors encountered during execution of the mutation.
"""
errors: [String!]!
"""
The updated iteration
"""
iteration: Iteration
}
""" """
Autogenerated input type of UpdateNote Autogenerated input type of UpdateNote
""" """
......
...@@ -23646,6 +23646,33 @@ ...@@ -23646,6 +23646,33 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "updateIteration",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "UpdateIterationInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "UpdateIterationPayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "updateNote", "name": "updateNote",
"description": "Updates a Note. If the body of the Note contains only quick actions, the Note will be destroyed during the update, and no Note will be returned", "description": "Updates a Note. If the body of the Note contains only quick actions, the Note will be destroyed during the update, and no Note will be returned",
...@@ -39167,6 +39194,162 @@ ...@@ -39167,6 +39194,162 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "INPUT_OBJECT",
"name": "UpdateIterationInput",
"description": "Autogenerated input type of UpdateIteration",
"fields": null,
"inputFields": [
{
"name": "groupPath",
"description": "The group of the iteration",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "id",
"description": "The id of the iteration",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "title",
"description": "The title of the iteration",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "description",
"description": "The description of the iteration",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "startDate",
"description": "The start date of the iteration",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "dueDate",
"description": "The end date of the iteration",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "UpdateIterationPayload",
"description": "Autogenerated return type of UpdateIteration",
"fields": [
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "errors",
"description": "Errors encountered during execution of the mutation.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "iteration",
"description": "The updated iteration",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "Iteration",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "INPUT_OBJECT", "kind": "INPUT_OBJECT",
"name": "UpdateNoteInput", "name": "UpdateNoteInput",
...@@ -2019,6 +2019,16 @@ Autogenerated return type of UpdateIssue ...@@ -2019,6 +2019,16 @@ Autogenerated return type of UpdateIssue
| `errors` | String! => Array | Errors encountered during execution of the mutation. | | `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `issue` | Issue | The issue after mutation | | `issue` | Issue | The issue after mutation |
## UpdateIterationPayload
Autogenerated return type of UpdateIteration
| Name | Type | Description |
| --- | ---- | ---------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `iteration` | Iteration | The updated iteration |
## UpdateNotePayload ## UpdateNotePayload
Autogenerated return type of UpdateNote Autogenerated return type of UpdateNote
......
...@@ -283,7 +283,7 @@ Markdown code will embed the test coverage report badge of the `coverage` job ...@@ -283,7 +283,7 @@ Markdown code will embed the test coverage report badge of the `coverage` job
into your `README.md`: into your `README.md`:
```markdown ```markdown
![coverage](https://gitlab.com/gitlab-org/gitlab-foss/badges/master/coverage.svg?job=coverage) ![coverage](https://gitlab.com/gitlab-org/gitlab/badges/master/coverage.svg?job=coverage)
``` ```
### Badge styles ### Badge styles
...@@ -296,7 +296,7 @@ Pipeline badges can be rendered in different styles by adding the `style=style_n ...@@ -296,7 +296,7 @@ Pipeline badges can be rendered in different styles by adding the `style=style_n
https://example.gitlab.com/<namespace>/<project>/badges/<branch>/coverage.svg?style=flat https://example.gitlab.com/<namespace>/<project>/badges/<branch>/coverage.svg?style=flat
``` ```
![Badge flat style](https://gitlab.com/gitlab-org/gitlab-foss/badges/master/coverage.svg?job=coverage&style=flat) ![Badge flat style](https://gitlab.com/gitlab-org/gitlab/badges/master/coverage.svg?job=coverage&style=flat)
#### Flat square #### Flat square
...@@ -306,17 +306,17 @@ https://example.gitlab.com/<namespace>/<project>/badges/<branch>/coverage.svg?st ...@@ -306,17 +306,17 @@ https://example.gitlab.com/<namespace>/<project>/badges/<branch>/coverage.svg?st
https://example.gitlab.com/<namespace>/<project>/badges/<branch>/coverage.svg?style=flat-square https://example.gitlab.com/<namespace>/<project>/badges/<branch>/coverage.svg?style=flat-square
``` ```
![Badge flat square style](https://gitlab.com/gitlab-org/gitlab-foss/badges/master/coverage.svg?job=coverage&style=flat-square) ![Badge flat square style](https://gitlab.com/gitlab-org/gitlab/badges/master/coverage.svg?job=coverage&style=flat-square)
### Custom badge text ### Custom badge text
The text for a badge can be customized. This can be useful to differentiate between multiple coverage jobs that run in the same pipeline. Customize the badge text and width by adding the `key_text=custom_text` and `key_width=custom_key_width` parameters to the URL: The text for a badge can be customized. This can be useful to differentiate between multiple coverage jobs that run in the same pipeline. Customize the badge text and width by adding the `key_text=custom_text` and `key_width=custom_key_width` parameters to the URL:
```plaintext ```plaintext
https://gitlab.com/gitlab-org/gitlab-foss/badges/master/coverage.svg?job=karma&key_text=Frontend+Coverage&key_width=100 https://gitlab.com/gitlab-org/gitlab/badges/master/coverage.svg?job=karma&key_text=Frontend+Coverage&key_width=100
``` ```
![Badge with custom text and width](https://gitlab.com/gitlab-org/gitlab-foss/badges/master/coverage.svg?job=karma&key_text=Frontend+Coverage&key_width=100) ![Badge with custom text and width](https://gitlab.com/gitlab-org/gitlab/badges/master/coverage.svg?job=karma&key_text=Frontend+Coverage&key_width=100)
## Environment Variables ## Environment Variables
......
...@@ -412,7 +412,7 @@ are *explicitly not supported* and may stop working at any time. ...@@ -412,7 +412,7 @@ are *explicitly not supported* and may stop working at any time.
### Options for Microsoft Windows ### Options for Microsoft Windows
If you're running Windows 10, the [Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install-win10), and its latest [WSL 2](https://docs.microsoft.com/en-us/install-win10) version, If you're running Windows 10, the [Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install-win10), and its latest [WSL 2](https://docs.microsoft.com/en-us/windows/wsl/install-win10#update-to-wsl-2) version,
support the installation of different Linux distributions, which include the Git and SSH clients. support the installation of different Linux distributions, which include the Git and SSH clients.
For current versions of Windows, you can also install the Git and SSH clients with For current versions of Windows, you can also install the Git and SSH clients with
......
...@@ -72,7 +72,7 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres ...@@ -72,7 +72,7 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres
1. [Getting Help](https://about.gitlab.com/get-help/) 1. [Getting Help](https://about.gitlab.com/get-help/)
- Proposing Features and Reporting and Tracking bugs for GitLab - Proposing Features and Reporting and Tracking bugs for GitLab
- The GitLab IRC channel, Gitter Chat Room, Community Forum and Mailing List - The GitLab IRC channel, Gitter Chat Room, Community Forum, and Mailing List
- Getting Technical Support - Getting Technical Support
- Being part of our Great Community and Contributing to GitLab - Being part of our Great Community and Contributing to GitLab
1. [Getting Started with the GitLab Development Kit (GDK)](https://about.gitlab.com/blog/2016/06/08/getting-started-with-gitlab-development-kit/) 1. [Getting Started with the GitLab Development Kit (GDK)](https://about.gitlab.com/blog/2016/06/08/getting-started-with-gitlab-development-kit/)
...@@ -140,11 +140,11 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres ...@@ -140,11 +140,11 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres
## 3. GitLab Advanced ## 3. GitLab Advanced
### 3.1. Dev Ops ### 3.1. DevOps
1. [Xebia Labs: Dev Ops Terminology](https://xebialabs.com/glossary/) 1. [XebiaLabs: DevOps Terminology](https://xebialabs.com/glossary/)
1. [Xebia Labs: Periodic Table of DevOps Tools](https://xebialabs.com/periodic-table-of-devops-tools/) 1. [XebiaLabs: Periodic Table of DevOps Tools](https://digital.ai/periodic-table-of-devops-tools)
1. [Puppet Labs: State of Dev Ops 2016 - Book](https://puppet.com/resources/report/2016-state-devops-report/) 1. [Puppet Labs: State of DevOps 2016 - Book](https://puppet.com/resources/report/2016-state-devops-report/)
### 3.2. Installing GitLab with Omnibus ### 3.2. Installing GitLab with Omnibus
......
...@@ -15,7 +15,7 @@ See the [book list](booklist.md) for additional recommendations. ...@@ -15,7 +15,7 @@ See the [book list](booklist.md) for additional recommendations.
1. **Remote: Office not required** 1. **Remote: Office not required**
David Heinemeier Hansson and Jason Fried, 2013 David Heinemeier Hansson and Jason Fried, 2013
([amazon](https://www.amazon.co.uk/Remote-Required-David-Heinemeier-Hansson/dp/0091954673)) ([Amazon](https://www.amazon.co.uk/dp/0091954673/ref=cm_sw_r_tw_dp_x_0yy9EbZ2WXJ6Y))
1. **The Year Without Pants** 1. **The Year Without Pants**
......
...@@ -72,7 +72,7 @@ Plans are underway for supporting the following languages, dependency managers, ...@@ -72,7 +72,7 @@ Plans are underway for supporting the following languages, dependency managers,
| Language (package managers) | Supported files | Scan tool(s) | Issue | | Language (package managers) | Supported files | Scan tool(s) | Issue |
|----------------------------- | --------------- | ------------ | ----- | |----------------------------- | --------------- | ------------ | ----- |
| Python ([Poetry](http://python-poetry.org/)) | `poetry.lock` | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) | [GitLab#7006](https://gitlab.com/gitlab-org/gitlab/-/issues/7006) | | Python ([Poetry](https://python-poetry.org/)) | `poetry.lock` | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) | [GitLab#7006](https://gitlab.com/gitlab-org/gitlab/-/issues/7006) |
| Python ([Pipenv](https://pipenv.pypa.io/en/latest/)) | `Pipfile.lock` | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) | [GitLab#11756](https://gitlab.com/gitlab-org/gitlab/-/issues/11756) | | Python ([Pipenv](https://pipenv.pypa.io/en/latest/)) | `Pipfile.lock` | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) | [GitLab#11756](https://gitlab.com/gitlab-org/gitlab/-/issues/11756) |
## Contribute your scanner ## Contribute your scanner
......
...@@ -1084,8 +1084,8 @@ customRules: ...@@ -1084,8 +1084,8 @@ customRules:
``` ```
By default, Falco only outputs security events to logs as JSON objects. To set it to output to an By default, Falco only outputs security events to logs as JSON objects. To set it to output to an
[external API](https://falco.org/docs/alerts#https-output-send-alerts-to-an-https-end-point) [external API](https://falco.org/docs/alerts/#https-output-send-alerts-to-an-https-end-point)
or [application](https://falco.org/docs/alerts#program-output), or [application](https://falco.org/docs/alerts/#program-output),
add the following to `.gitlab/managed-apps/falco/values.yaml`: add the following to `.gitlab/managed-apps/falco/values.yaml`:
```yaml ```yaml
......
...@@ -64,7 +64,7 @@ The following languages and package managers are supported. ...@@ -64,7 +64,7 @@ The following languages and package managers are supported.
| Go | [Godep](https://github.com/tools/godep), [go mod](https://github.com/golang/go/wiki/Modules) |[License Finder](https://github.com/pivotal/LicenseFinder)| | Go | [Godep](https://github.com/tools/godep), [go mod](https://github.com/golang/go/wiki/Modules) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| Java | [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)| | Java | [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| .NET | [Nuget](https://www.nuget.org/) (.NET Framework is supported via the [mono project](https://www.mono-project.com/). Windows specific dependencies are not supported at this time.) |[License Finder](https://github.com/pivotal/LicenseFinder)| | .NET | [Nuget](https://www.nuget.org/) (.NET Framework is supported via the [mono project](https://www.mono-project.com/). Windows specific dependencies are not supported at this time.) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| Python | [pip](https://pip.pypa.io/en/stable/) (Python is supported through [requirements.txt](https://pip.pypa.io/en/1.1/requirements/) and [Pipfile.lock](https://github.com/pypa/pipfile#pipfilelock).) |[License Finder](https://github.com/pivotal/LicenseFinder)| | Python | [pip](https://pip.pypa.io/en/stable/) (Python is supported through [requirements.txt](https://pip.pypa.io/en/stable/user_guide/#requirements-files) and [Pipfile.lock](https://github.com/pypa/pipfile#pipfilelock).) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| Ruby | [gem](https://rubygems.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)| | Ruby | [gem](https://rubygems.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| Objective-C, Swift | [Carthage](https://github.com/Carthage/Carthage) |[License Finder](https://github.com/pivotal/LicenseFinder)| | Objective-C, Swift | [Carthage](https://github.com/Carthage/Carthage) |[License Finder](https://github.com/pivotal/LicenseFinder)|
...@@ -339,7 +339,7 @@ strict-ssl = false ...@@ -339,7 +339,7 @@ strict-ssl = false
### Configuring Yarn projects ### Configuring Yarn projects
You can configure Yarn projects by using a [`.yarnrc.yml`](https://yarnpkg.com/configuration/yarnrc/) You can configure Yarn projects by using a [`.yarnrc.yml`](https://yarnpkg.com/configuration/yarnrc)
file. file.
#### Using private Yarn registries #### Using private Yarn registries
......
...@@ -38,7 +38,7 @@ From there you can create a new iteration or click an iteration to get a more de ...@@ -38,7 +38,7 @@ From there you can create a new iteration or click an iteration to get a more de
## Create an iteration ## Create an iteration
NOTE: **Note:** NOTE: **Note:**
A permission level of [Developer or higher](../../permissions.md) is required to create iterations. You need Developer [permissions](../../permissions.md) or higher to create an iteration.
To create an iteration: To create an iteration:
...@@ -47,7 +47,16 @@ To create an iteration: ...@@ -47,7 +47,16 @@ To create an iteration:
1. Enter the title, a description (optional), a start date, and a due date. 1. Enter the title, a description (optional), a start date, and a due date.
1. Click **Create iteration**. The iteration details page opens. 1. Click **Create iteration**. The iteration details page opens.
### Enable Iterations **(CORE ONLY)** ## Edit an iteration
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218277) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.2.
NOTE: **Note:**
You need Developer [permissions](../../permissions.md) or higher to edit an iteration.
To edit an iteration, click the three-dot menu (**{ellipsis_v}**) > **Edit iteration**.
## Enable Iterations **(CORE ONLY)**
GitLab Iterations feature is under development and not ready for production use. GitLab Iterations feature is under development and not ready for production use.
It is deployed behind a feature flag that is **disabled by default**. It is deployed behind a feature flag that is **disabled by default**.
......
...@@ -75,7 +75,7 @@ This is a [Closed Beta](https://about.gitlab.com/handbook/product/#closed-beta) ...@@ -75,7 +75,7 @@ This is a [Closed Beta](https://about.gitlab.com/handbook/product/#closed-beta)
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/709) in GitLab 12.1. > - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/709) in GitLab 12.1.
> - It's deployed behind a feature flag, disabled by default. > - It's deployed behind a feature flag, disabled by default.
> - It's disabled on GitLab.com. If you want to enable it for your subscription, contact Product Management through [GitLab Support](https://support.gitlab.com/hc). > - It's disabled on GitLab.com. If you want to enable it for your subscription, contact Product Management through [GitLab Support](https://support.gitlab.com/hc/en-us).
When SSO is being enforced, groups can enable an additional level of protection by enforcing the creation of dedicated user accounts to access the group. When SSO is being enforced, groups can enable an additional level of protection by enforcing the creation of dedicated user accounts to access the group.
......
...@@ -54,7 +54,7 @@ repository that were written using some of the nuances of GitLab's RedCarpet ver ...@@ -54,7 +54,7 @@ repository that were written using some of the nuances of GitLab's RedCarpet ver
of Markdown. Since CommonMark uses slightly stricter syntax, these documents of Markdown. Since CommonMark uses slightly stricter syntax, these documents
might now appear a little differently since we have transitioned to CommonMark. might now appear a little differently since we have transitioned to CommonMark.
It's usually quite easy to fix. For example, numbered lists with nested lists may For example, numbered lists with nested lists may
render incorrectly: render incorrectly:
```markdown ```markdown
...@@ -63,8 +63,8 @@ render incorrectly: ...@@ -63,8 +63,8 @@ render incorrectly:
- milk - milk
``` ```
Simply add a space to each nested item to align the `-` with the first character of To correct their rendering, add a space to each nested item to align the `-` with the first
the top list item (`C` in this case): character of the top list item (`C` in this case):
```markdown ```markdown
1. Chocolate 1. Chocolate
...@@ -241,7 +241,7 @@ Sometimes you want to :monkey: around a bit and add some :star2: to your :speech ...@@ -241,7 +241,7 @@ Sometimes you want to :monkey: around a bit and add some :star2: to your :speech
You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that. You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that.
If you're new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up one of the supported codes. If you're new to this, don't be :fearful:. You can join the emoji :family:. All you need to do is to look up one of the supported codes.
Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup: Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
``` ```
...@@ -252,7 +252,7 @@ Sometimes you want to <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/ma ...@@ -252,7 +252,7 @@ Sometimes you want to <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/ma
You can use it to point out a <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/bug.png" width="20px" height="20px" style="display:inline;margin:0"> or warn about <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/speak_no_evil.png" width="20px" height="20px" style="display:inline;margin:0"> patches. And if someone improves your really <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/snail.png" width="20px" height="20px" style="display:inline;margin:0"> code, send them some <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/birthday.png" width="20px" height="20px" style="display:inline;margin:0">. People will <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/heart.png" width="20px" height="20px" style="display:inline;margin:0"> you for that. You can use it to point out a <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/bug.png" width="20px" height="20px" style="display:inline;margin:0"> or warn about <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/speak_no_evil.png" width="20px" height="20px" style="display:inline;margin:0"> patches. And if someone improves your really <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/snail.png" width="20px" height="20px" style="display:inline;margin:0"> code, send them some <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/birthday.png" width="20px" height="20px" style="display:inline;margin:0">. People will <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/heart.png" width="20px" height="20px" style="display:inline;margin:0"> you for that.
If you're new to this, don't be <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/fearful.png" width="20px" height="20px" style="display:inline;margin:0">. You can easily join the emoji <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/family.png" width="20px" height="20px" style="display:inline;margin:0">. All you need to do is to look up one of the supported codes. If you're new to this, don't be <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/fearful.png" width="20px" height="20px" style="display:inline;margin:0">. You can join the emoji <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/family.png" width="20px" height="20px" style="display:inline;margin:0">. All you need to do is to look up one of the supported codes.
Consult the [Emoji Cheat Sheet](https://www.webfx.com/tools/emoji-cheat-sheet/) for a list of all supported emoji codes. <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/thumbsup.png" width="20px" height="20px" style="display:inline;margin:0"> Consult the [Emoji Cheat Sheet](https://www.webfx.com/tools/emoji-cheat-sheet/) for a list of all supported emoji codes. <img src="https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/assets/images/emoji/thumbsup.png" width="20px" height="20px" style="display:inline;margin:0">
...@@ -407,9 +407,9 @@ the [Asciidoctor user manual](https://asciidoctor.org/docs/user-manual/#activati ...@@ -407,9 +407,9 @@ the [Asciidoctor user manual](https://asciidoctor.org/docs/user-manual/#activati
### Special GitLab references ### Special GitLab references
GFM recognizes special GitLab related references. For example, you can easily reference GFM recognizes special GitLab related references. For example, you can reference
an issue, a commit, a team member, or even the whole team within a project. GFM will turn an issue, a commit, a team member, or even the whole team within a project. GFM will turn
that reference into a link so you can navigate between them easily. that reference into a link so you can navigate between them.
Additionally, GFM recognizes certain cross-project references and also has a shorthand Additionally, GFM recognizes certain cross-project references and also has a shorthand
version to reference other projects from the same namespace. version to reference other projects from the same namespace.
...@@ -589,7 +589,7 @@ If a functionality is extended, the new option will be listed as a sub-section. ...@@ -589,7 +589,7 @@ If a functionality is extended, the new option will be listed as a sub-section.
### Blockquotes ### Blockquotes
Blockquotes are an easy way to highlight information, such as a side-note. It's generated Blockquotes are useful to highlight information, such as a side-note. It's generated
by starting the lines of the blockquote with `>`: by starting the lines of the blockquote with `>`:
```markdown ```markdown
...@@ -635,9 +635,9 @@ you can quote that without having to manually prepend `>` to every line! ...@@ -635,9 +635,9 @@ you can quote that without having to manually prepend `>` to every line!
### Code spans and blocks ### Code spans and blocks
You can easily highlight anything that should be viewed as code and not simple text. You can highlight anything that should be viewed as code and not simple text.
Simple inline code is easily highlighted with single backticks `` ` ``: Simple inline code is highlighted with single backticks `` ` ``:
```markdown ```markdown
Inline `code` has `back-ticks around` it. Inline `code` has `back-ticks around` it.
...@@ -1148,7 +1148,7 @@ A line break will be inserted (a new paragraph will start) if the previous text ...@@ -1148,7 +1148,7 @@ A line break will be inserted (a new paragraph will start) if the previous text
ended with two newlines, like when you hit <kbd>Enter</kbd> twice in a row. If you only ended with two newlines, like when you hit <kbd>Enter</kbd> twice in a row. If you only
use one newline (hit <kbd>Enter</kbd> once), the next sentence will be part of the use one newline (hit <kbd>Enter</kbd> once), the next sentence will be part of the
same paragraph. This is useful if you want to keep long lines from wrapping, and keep same paragraph. This is useful if you want to keep long lines from wrapping, and keep
them easily editable: them editable:
```markdown ```markdown
Here's a line for us to start with. Here's a line for us to start with.
...@@ -1273,7 +1273,7 @@ GFM will auto-link almost any URL you put into your text: ...@@ -1273,7 +1273,7 @@ GFM will auto-link almost any URL you put into your text:
### Lists ### Lists
Ordered and unordered lists can be easily created. Ordered and unordered lists can be created.
For an ordered list, add the number you want the list For an ordered list, add the number you want the list
to start with, like `1.`, followed by a space, at the start of each line for ordered lists. to start with, like `1.`, followed by a space, at the start of each line for ordered lists.
......
...@@ -247,6 +247,7 @@ group. ...@@ -247,6 +247,7 @@ group.
| Create project in group | | | ✓ (3) | ✓ (3) | ✓ (3) | | Create project in group | | | ✓ (3) | ✓ (3) | ✓ (3) |
| Share (invite) groups with groups | | | | | ✓ | | Share (invite) groups with groups | | | | | ✓ |
| Create/edit/delete group milestones | | | ✓ | ✓ | ✓ | | Create/edit/delete group milestones | | | ✓ | ✓ | ✓ |
| Create/edit/delete iterations | | | ✓ | ✓ | ✓ |
| Enable/disable a dependency proxy **(PREMIUM)** | | | ✓ | ✓ | ✓ | | Enable/disable a dependency proxy **(PREMIUM)** | | | ✓ | ✓ | ✓ |
| Use security dashboard **(ULTIMATE)** | | | ✓ | ✓ | ✓ | | Use security dashboard **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
| Create/edit/delete metrics dashboard annotations | | | ✓ | ✓ | ✓ | | Create/edit/delete metrics dashboard annotations | | | ✓ | ✓ | ✓ |
......
...@@ -1050,8 +1050,7 @@ Alerts can be used to trigger actions, like opening an issue automatically (disa ...@@ -1050,8 +1050,7 @@ Alerts can be used to trigger actions, like opening an issue automatically (disa
1. Optionally, select whether to send an email notification to the developers of the project. 1. Optionally, select whether to send an email notification to the developers of the project.
1. Click **Save changes**. 1. Click **Save changes**.
Once enabled, an issue will be opened automatically when an alert is triggered which contains values extracted from [alert's payload](https://prometheus.io/docs/alerting/configuration/#webhook_config Once enabled, an issue will be opened automatically when an alert is triggered which contains values extracted from [alert's payload](https://prometheus.io/docs/alerting/latest/configuration/#webhook_config):
):
- Issue author: `GitLab Alert Bot` - Issue author: `GitLab Alert Bot`
- Issue title: Extract from `annotations/title`, `annotations/summary` or `labels/alertname` - Issue title: Extract from `annotations/title`, `annotations/summary` or `labels/alertname`
......
# Publishes a tag/branch to Composer Packages of the current project
publish:
image: curlimages/curl:latest
stage: build
variables:
URL: "$CI_SERVER_PROTOCOL://$CI_SERVER_HOST:$CI_SERVER_PORT/api/v4/projects/$CI_PROJECT_ID/packages/composer?job_token=$CI_JOB_TOKEN"
script:
- version=$([[ -z "$CI_COMMIT_TAG" ]] && echo "branch=$CI_COMMIT_REF_NAME" || echo "tag=$CI_COMMIT_TAG")
- insecure=$([ "$CI_SERVER_PROTOCOL" = "http" ] && echo "--insecure" || echo "")
- response=$(curl -s -w "\n%{http_code}" $insecure --data $version $URL)
- code=$(echo "$response" | tail -n 1)
- body=$(echo "$response" | head -n 1)
# Output state information
- if [ $code -eq 201 ]; then
echo "Package created - Code $code - $body";
else
echo "Could not create package - Code $code - $body";
exit 1;
fi
...@@ -1233,6 +1233,9 @@ msgstr "" ...@@ -1233,6 +1233,9 @@ msgstr ""
msgid "Action to take when receiving an alert." msgid "Action to take when receiving an alert."
msgstr "" msgstr ""
msgid "Actions"
msgstr ""
msgid "Activate" msgid "Activate"
msgstr "" msgstr ""
...@@ -2254,6 +2257,9 @@ msgstr "" ...@@ -2254,6 +2257,9 @@ msgstr ""
msgid "An error occurred when updating the issue weight" msgid "An error occurred when updating the issue weight"
msgstr "" msgstr ""
msgid "An error occurred while acknowledging the notification. Refresh the page and try again."
msgstr ""
msgid "An error occurred while adding formatted title for epic" msgid "An error occurred while adding formatted title for epic"
msgstr "" msgstr ""
...@@ -8231,6 +8237,9 @@ msgstr "" ...@@ -8231,6 +8237,9 @@ msgstr ""
msgid "Edit issues" msgid "Edit issues"
msgstr "" msgstr ""
msgid "Edit iteration"
msgstr ""
msgid "Edit public deploy key" msgid "Edit public deploy key"
msgstr "" msgstr ""
...@@ -9509,6 +9518,12 @@ msgstr "" ...@@ -9509,6 +9518,12 @@ msgstr ""
msgid "Failed to install." msgid "Failed to install."
msgstr "" msgstr ""
msgid "Failed to load assignees. Please try again."
msgstr ""
msgid "Failed to load authors. Please try again."
msgstr ""
msgid "Failed to load emoji list." msgid "Failed to load emoji list."
msgstr "" msgstr ""
...@@ -12662,6 +12677,9 @@ msgstr "" ...@@ -12662,6 +12677,9 @@ msgstr ""
msgid "Iteration removed" msgid "Iteration removed"
msgstr "" msgstr ""
msgid "Iteration updated"
msgstr ""
msgid "Iterations" msgid "Iterations"
msgstr "" msgstr ""
...@@ -14885,9 +14903,6 @@ msgid_plural "New Issues" ...@@ -14885,9 +14903,6 @@ msgid_plural "New Issues"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "New Iteration"
msgstr ""
msgid "New Jira import" msgid "New Jira import"
msgstr "" msgstr ""
...@@ -24515,6 +24530,9 @@ msgstr "" ...@@ -24515,6 +24530,9 @@ msgstr ""
msgid "Update it" msgid "Update it"
msgstr "" msgstr ""
msgid "Update iteration"
msgstr ""
msgid "Update now" msgid "Update now"
msgstr "" msgstr ""
......
...@@ -43,6 +43,23 @@ describe('PersistentUserCallout', () => { ...@@ -43,6 +43,23 @@ describe('PersistentUserCallout', () => {
return fixture; return fixture;
} }
function createFollowLinkFixture() {
const fixture = document.createElement('div');
fixture.innerHTML = `
<ul>
<li
class="container"
data-dismiss-endpoint="${dismissEndpoint}"
data-feature-id="${featureName}"
>
<a class="js-follow-link" href="/somewhere-pleasant">A Link</a>
</li>
</ul>
`;
return fixture;
}
describe('dismiss', () => { describe('dismiss', () => {
let button; let button;
let mockAxios; let mockAxios;
...@@ -144,6 +161,55 @@ describe('PersistentUserCallout', () => { ...@@ -144,6 +161,55 @@ describe('PersistentUserCallout', () => {
}); });
}); });
describe('follow links', () => {
let link;
let mockAxios;
let persistentUserCallout;
beforeEach(() => {
const fixture = createFollowLinkFixture();
const container = fixture.querySelector('.container');
link = fixture.querySelector('.js-follow-link');
mockAxios = new MockAdapter(axios);
persistentUserCallout = new PersistentUserCallout(container);
jest.spyOn(persistentUserCallout.container, 'remove').mockImplementation(() => {});
delete window.location;
window.location = { assign: jest.fn() };
});
afterEach(() => {
mockAxios.restore();
});
it('uses a link to trigger callout and defers following until callout is finished', () => {
const { href } = link;
mockAxios.onPost(dismissEndpoint).replyOnce(200);
link.click();
return waitForPromises().then(() => {
expect(window.location.assign).toBeCalledWith(href);
expect(mockAxios.history.post[0].data).toBe(JSON.stringify({ feature_name: featureName }));
expect(persistentUserCallout.container.remove).not.toHaveBeenCalled();
});
});
it('invokes Flash when the dismiss request fails', () => {
mockAxios.onPost(dismissEndpoint).replyOnce(500);
link.click();
return waitForPromises().then(() => {
expect(window.location.assign).not.toHaveBeenCalled();
expect(Flash).toHaveBeenCalledWith(
'An error occurred while acknowledging the notification. Refresh the page and try again.',
);
});
});
});
describe('factory', () => { describe('factory', () => {
it('returns an instance of PersistentUserCallout with the provided container property', () => { it('returns an instance of PersistentUserCallout with the provided container property', () => {
const fixture = createFixture(); const fixture = createFixture();
......
...@@ -45,6 +45,14 @@ RSpec.describe Iteration do ...@@ -45,6 +45,14 @@ RSpec.describe Iteration do
it { is_expected.to be_valid } it { is_expected.to be_valid }
end end
context 'when updated iteration dates overlap with its own dates' do
it 'is valid' do
existing_iteration.start_date = 5.days.from_now
expect(existing_iteration).to be_valid
end
end
context 'when dates overlap' do context 'when dates overlap' do
context 'same group' do context 'same group' do
context 'when start_date is in range' do context 'when start_date is in range' do
......
...@@ -7,6 +7,8 @@ RSpec.describe MergeRequests::PostMergeService do ...@@ -7,6 +7,8 @@ RSpec.describe MergeRequests::PostMergeService do
let(:merge_request) { create(:merge_request, assignees: [user]) } let(:merge_request) { create(:merge_request, assignees: [user]) }
let(:project) { merge_request.project } let(:project) { merge_request.project }
subject { described_class.new(project, user).execute(merge_request) }
before do before do
project.add_maintainer(user) project.add_maintainer(user)
end end
...@@ -19,10 +21,7 @@ RSpec.describe MergeRequests::PostMergeService do ...@@ -19,10 +21,7 @@ RSpec.describe MergeRequests::PostMergeService do
project.open_merge_requests_count project.open_merge_requests_count
merge_request.update!(state: 'merged') merge_request.update!(state: 'merged')
service = described_class.new(project, user, {}) expect { subject }.to change { project.open_merge_requests_count }.from(1).to(0)
expect { service.execute(merge_request) }
.to change { project.open_merge_requests_count }.from(1).to(0)
end end
it 'updates metrics' do it 'updates metrics' do
...@@ -35,7 +34,7 @@ RSpec.describe MergeRequests::PostMergeService do ...@@ -35,7 +34,7 @@ RSpec.describe MergeRequests::PostMergeService do
expect(metrics_service).to receive(:merge) expect(metrics_service).to receive(:merge)
described_class.new(project, user, {}).execute(merge_request) subject
end end
it 'deletes non-latest diffs' do it 'deletes non-latest diffs' do
...@@ -45,7 +44,7 @@ RSpec.describe MergeRequests::PostMergeService do ...@@ -45,7 +44,7 @@ RSpec.describe MergeRequests::PostMergeService do
.to receive(:new).with(merge_request) .to receive(:new).with(merge_request)
.and_return(diff_removal_service) .and_return(diff_removal_service)
described_class.new(project, user, {}).execute(merge_request) subject
expect(diff_removal_service).to have_received(:execute) expect(diff_removal_service).to have_received(:execute)
end end
...@@ -56,21 +55,59 @@ RSpec.describe MergeRequests::PostMergeService do ...@@ -56,21 +55,59 @@ RSpec.describe MergeRequests::PostMergeService do
issue = create(:issue, project: project) issue = create(:issue, project: project)
allow(merge_request).to receive(:visible_closing_issues_for).and_return([issue]) allow(merge_request).to receive(:visible_closing_issues_for).and_return([issue])
expect_next_instance_of(Issues::CloseService) do |service| expect_next_instance_of(Issues::CloseService) do |close_service|
allow(service).to receive(:execute).with(issue, commit: merge_request).and_raise(RuntimeError) allow(close_service).to receive(:execute).with(issue, commit: merge_request).and_raise(RuntimeError)
end end
expect { described_class.new(project, user).execute(merge_request) }.to raise_error(RuntimeError) expect { subject }.to raise_error(RuntimeError)
expect(merge_request.reload).to be_merged expect(merge_request.reload).to be_merged
end end
it 'clean up environments for the merge request' do it 'clean up environments for the merge request' do
expect_next_instance_of(Ci::StopEnvironmentsService) do |service| expect_next_instance_of(Ci::StopEnvironmentsService) do |stop_environment_service|
expect(service).to receive(:execute_for_merge_request).with(merge_request) expect(stop_environment_service).to receive(:execute_for_merge_request).with(merge_request)
end end
described_class.new(project, user).execute(merge_request) subject
end
context 'when the merge request has review apps' do
it 'cancels all review app deployments' do
pipeline = create(:ci_pipeline,
source: :merge_request_event,
merge_request: merge_request,
project: project,
sha: merge_request.diff_head_sha,
merge_requests_as_head_pipeline: [merge_request])
review_env_a = create(:environment, project: project, state: :available, name: 'review/a')
review_env_b = create(:environment, project: project, state: :available, name: 'review/b')
review_env_c = create(:environment, project: project, state: :stopped, name: 'review/c')
deploy_env = create(:environment, project: project, state: :available, name: 'deploy')
review_job_a1 = create(:ci_build, :with_deployment, :start_review_app,
pipeline: pipeline, project: project, environment: review_env_a.name)
review_job_a2 = create(:ci_build, :with_deployment, :start_review_app,
pipeline: pipeline, project: project, environment: review_env_a.name)
review_job_b1 = create(:ci_build, :with_deployment, :start_review_app,
pipeline: pipeline, project: project, environment: review_env_b.name)
review_job_b2 = create(:ci_build, :start_review_app,
pipeline: pipeline, project: project, environment: review_env_b.name)
review_job_c1 = create(:ci_build, :with_deployment, :start_review_app,
pipeline: pipeline, project: project, environment: review_env_c.name)
deploy_job = create(:ci_build, :with_deployment, :deploy_to_production,
pipeline: pipeline, project: project, environment: deploy_env.name)
subject
expect(review_job_a1.reload.canceled?).to be true
expect(review_job_a2.reload.canceled?).to be true
expect(review_job_b1.reload.canceled?).to be true
expect(review_job_b2.reload.canceled?).to be false
expect(review_job_c1.reload.canceled?).to be false
expect(deploy_job.reload.canceled?).to be false
end
end end
end end
end end
...@@ -211,8 +211,26 @@ RSpec.shared_examples 'wiki controller actions' do ...@@ -211,8 +211,26 @@ RSpec.shared_examples 'wiki controller actions' do
end end
end end
describe 'GET #edit' do shared_examples 'edit action' do
subject { get(:edit, params: routing_params.merge(id: wiki_title)) } context 'when the page does not exist' do
let(:id_param) { 'invalid' }
it 'redirects to show' do
subject
expect(response).to redirect_to_wiki(wiki, 'invalid')
end
end
context 'when id param is blank' do
let(:id_param) { ' ' }
it 'redirects to the home page' do
subject
expect(response).to redirect_to_wiki(wiki, 'home')
end
end
context 'when page content encoding is invalid' do context 'when page content encoding is invalid' do
it 'redirects to show' do it 'redirects to show' do
...@@ -236,6 +254,14 @@ RSpec.shared_examples 'wiki controller actions' do ...@@ -236,6 +254,14 @@ RSpec.shared_examples 'wiki controller actions' do
expect(response).to redirect_to_wiki(wiki, page) expect(response).to redirect_to_wiki(wiki, page)
end end
end end
end
describe 'GET #edit' do
let(:id_param) { wiki_title }
subject { get(:edit, params: routing_params.merge(id: id_param)) }
it_behaves_like 'edit action'
context 'when page content encoding is valid' do context 'when page content encoding is valid' do
render_views render_views
...@@ -252,23 +278,17 @@ RSpec.shared_examples 'wiki controller actions' do ...@@ -252,23 +278,17 @@ RSpec.shared_examples 'wiki controller actions' do
describe 'PATCH #update' do describe 'PATCH #update' do
let(:new_title) { 'New title' } let(:new_title) { 'New title' }
let(:new_content) { 'New content' } let(:new_content) { 'New content' }
let(:id_param) { wiki_title }
subject do subject do
patch(:update, patch(:update,
params: routing_params.merge( params: routing_params.merge(
id: wiki_title, id: id_param,
wiki: { title: new_title, content: new_content } wiki: { title: new_title, content: new_content }
)) ))
end end
context 'when page content encoding is invalid' do it_behaves_like 'edit action'
it 'redirects to show' do
allow(controller).to receive(:valid_encoding?).and_return(false)
subject
expect(response).to redirect_to_wiki(wiki, wiki.list_pages.first)
end
end
context 'when page content encoding is valid' do context 'when page content encoding is valid' do
render_views render_views
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册