Commit 2349eabc authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 134fe182
...@@ -18,8 +18,6 @@ import Icon from '~/vue_shared/components/icon.vue'; ...@@ -18,8 +18,6 @@ import Icon from '~/vue_shared/components/icon.vue';
import { getParameterValues, mergeUrlParams, redirectTo } from '~/lib/utils/url_utility'; import { getParameterValues, mergeUrlParams, redirectTo } from '~/lib/utils/url_utility';
import invalidUrl from '~/lib/utils/invalid_url'; import invalidUrl from '~/lib/utils/invalid_url';
import DateTimePicker from './date_time_picker/date_time_picker.vue'; import DateTimePicker from './date_time_picker/date_time_picker.vue';
import MonitorTimeSeriesChart from './charts/time_series.vue';
import MonitorSingleStatChart from './charts/single_stat.vue';
import GraphGroup from './graph_group.vue'; import GraphGroup from './graph_group.vue';
import EmptyState from './empty_state.vue'; import EmptyState from './empty_state.vue';
import TrackEventDirective from '~/vue_shared/directives/track_event'; import TrackEventDirective from '~/vue_shared/directives/track_event';
...@@ -28,8 +26,6 @@ import { getTimeDiff, isValidDate, getAddMetricTrackingOptions } from '../utils' ...@@ -28,8 +26,6 @@ import { getTimeDiff, isValidDate, getAddMetricTrackingOptions } from '../utils'
export default { export default {
components: { components: {
VueDraggable, VueDraggable,
MonitorTimeSeriesChart,
MonitorSingleStatChart,
PanelType, PanelType,
GraphGroup, GraphGroup,
EmptyState, EmptyState,
......
<script> <script>
import { mapActions, mapState } from 'vuex'; import { mapActions, mapState } from 'vuex';
import { getParameterValues, removeParams } from '~/lib/utils/url_utility'; import { getParameterValues, removeParams } from '~/lib/utils/url_utility';
import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue';
import GraphGroup from './graph_group.vue'; import GraphGroup from './graph_group.vue';
import MonitorTimeSeriesChart from './charts/time_series.vue';
import { sidebarAnimationDuration } from '../constants'; import { sidebarAnimationDuration } from '../constants';
import { getTimeDiff } from '../utils'; import { getTimeDiff } from '../utils';
...@@ -11,7 +11,7 @@ let sidebarMutationObserver; ...@@ -11,7 +11,7 @@ let sidebarMutationObserver;
export default { export default {
components: { components: {
GraphGroup, GraphGroup,
MonitorTimeSeriesChart, PanelType,
}, },
props: { props: {
dashboardUrl: { dashboardUrl: {
...@@ -92,16 +92,13 @@ export default { ...@@ -92,16 +92,13 @@ export default {
<template> <template>
<div class="metrics-embed" :class="{ 'd-inline-flex col-lg-6 p-0': isSingleChart }"> <div class="metrics-embed" :class="{ 'd-inline-flex col-lg-6 p-0': isSingleChart }">
<div v-if="charts.length" class="row w-100 m-n2 pb-4"> <div v-if="charts.length" class="row w-100 m-n2 pb-4">
<monitor-time-series-chart <panel-type
v-for="graphData in charts" v-for="(graphData, graphIndex) in charts"
:key="graphData.title" :key="`panel-type-${graphIndex}`"
class="w-100" class="w-100"
clipboard-text=""
:graph-data="graphData" :graph-data="graphData"
:container-width="elWidth"
:group-id="dashboardUrl" :group-id="dashboardUrl"
:project-path="null"
:show-border="true"
:single-embed="isSingleChart"
/> />
</div> </div>
</div> </div>
......
...@@ -47,6 +47,11 @@ export default { ...@@ -47,6 +47,11 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
groupId: {
type: String,
required: false,
default: 'panel-type-chart',
},
}, },
computed: { computed: {
...mapState('monitoringDashboard', ['deploymentData', 'projectPath']), ...mapState('monitoringDashboard', ['deploymentData', 'projectPath']),
...@@ -117,7 +122,7 @@ export default { ...@@ -117,7 +122,7 @@ export default {
:deployment-data="deploymentData" :deployment-data="deploymentData"
:project-path="projectPath" :project-path="projectPath"
:thresholds="getGraphAlertValues(graphData.metrics)" :thresholds="getGraphAlertValues(graphData.metrics)"
group-id="panel-type-chart" :group-id="groupId"
> >
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<alert-widget <alert-widget
......
...@@ -5,11 +5,9 @@ import initNotes from '~/init_notes'; ...@@ -5,11 +5,9 @@ import initNotes from '~/init_notes';
import snippetEmbed from '~/snippet/snippet_embed'; import snippetEmbed from '~/snippet/snippet_embed';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
if (!gon.features.snippetsVue) { new LineHighlighter(); // eslint-disable-line no-new
new LineHighlighter(); // eslint-disable-line no-new new BlobViewer(); // eslint-disable-line no-new
new BlobViewer(); // eslint-disable-line no-new initNotes();
initNotes(); new ZenMode(); // eslint-disable-line no-new
new ZenMode(); // eslint-disable-line no-new snippetEmbed();
snippetEmbed();
}
}); });
...@@ -28,6 +28,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic ...@@ -28,6 +28,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
positions = @merge_request.note_positions_for_paths(diffs.diff_file_paths, current_user) positions = @merge_request.note_positions_for_paths(diffs.diff_file_paths, current_user)
diffs.unfold_diff_files(positions.unfoldable) diffs.unfold_diff_files(positions.unfoldable)
diffs.write_cache
options = { options = {
merge_request: @merge_request, merge_request: @merge_request,
......
...@@ -34,3 +34,5 @@ module Emails ...@@ -34,3 +34,5 @@ module Emails
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
end end
end end
Emails::Profile.prepend_if_ee('EE::Emails::Profile')
...@@ -5,6 +5,7 @@ module Ci ...@@ -5,6 +5,7 @@ module Ci
# We should migrate this object to actual database record in the future # We should migrate this object to actual database record in the future
class LegacyStage class LegacyStage
include StaticModel include StaticModel
include Presentable
attr_reader :pipeline, :name attr_reader :pipeline, :name
......
...@@ -27,8 +27,6 @@ class MergeRequest < ApplicationRecord ...@@ -27,8 +27,6 @@ class MergeRequest < ApplicationRecord
SORTING_PREFERENCE_FIELD = :merge_requests_sort SORTING_PREFERENCE_FIELD = :merge_requests_sort
prepend_if_ee('::EE::MergeRequest') # rubocop: disable Cop/InjectEnterpriseEditionModule
belongs_to :target_project, class_name: "Project" belongs_to :target_project, class_name: "Project"
belongs_to :source_project, class_name: "Project" belongs_to :source_project, class_name: "Project"
belongs_to :merge_user, class_name: "User" belongs_to :merge_user, class_name: "User"
...@@ -1531,3 +1529,5 @@ class MergeRequest < ApplicationRecord ...@@ -1531,3 +1529,5 @@ class MergeRequest < ApplicationRecord
Gitlab::EtagCaching::Store.new.touch(key) Gitlab::EtagCaching::Store.new.touch(key)
end end
end end
MergeRequest.prepend_if_ee('::EE::MergeRequest')
...@@ -70,3 +70,5 @@ class PersonalAccessToken < ApplicationRecord ...@@ -70,3 +70,5 @@ class PersonalAccessToken < ApplicationRecord
"gitlab:personal_access_token:#{user_id}" "gitlab:personal_access_token:#{user_id}"
end end
end end
PersonalAccessToken.prepend_if_ee('EE::PersonalAccessToken')
# frozen_string_literal: true
module Ci
class LegacyStagePresenter < Gitlab::View::Presenter::Delegated
presents :legacy_stage
def latest_ordered_statuses
preload_statuses(legacy_stage.statuses.latest_ordered)
end
def retried_ordered_statuses
preload_statuses(legacy_stage.statuses.retried_ordered)
end
private
def preload_statuses(statuses)
statuses.tap do |statuses|
# rubocop: disable CodeReuse/ActiveRecord
ActiveRecord::Associations::Preloader.new.preload(preloadable_statuses(statuses), :tags)
# rubocop: enable CodeReuse/ActiveRecord
end
end
def preloadable_statuses(statuses)
statuses.reject do |status|
status.instance_of?(::GenericCommitStatus) || status.instance_of?(::Ci::Bridge)
end
end
end
end
...@@ -23,6 +23,9 @@ ...@@ -23,6 +23,9 @@
= f.label :session_expire_delay, _('Session duration (minutes)'), class: 'label-light' = f.label :session_expire_delay, _('Session duration (minutes)'), class: 'label-light'
= f.number_field :session_expire_delay, class: 'form-control' = f.number_field :session_expire_delay, class: 'form-control'
%span.form-text.text-muted#session_expire_delay_help_block= _('GitLab restart is required to apply changes') %span.form-text.text-muted#session_expire_delay_help_block= _('GitLab restart is required to apply changes')
= render_if_exists 'admin/application_settings/personal_access_token_expiration_policy', form: f
.form-group .form-group
= f.label :user_oauth_applications, _('User OAuth applications'), class: 'label-bold' = f.label :user_oauth_applications, _('User OAuth applications'), class: 'label-bold'
.form-check .form-check
......
- stage = stage.present(current_user: current_user)
%tr %tr
%th{ colspan: 10 } %th{ colspan: 10 }
%strong %strong
...@@ -6,8 +8,8 @@ ...@@ -6,8 +8,8 @@
= ci_icon_for_status(stage.status) = ci_icon_for_status(stage.status)
&nbsp; &nbsp;
= stage.name.titleize = stage.name.titleize
= render stage.statuses.latest_ordered, stage: false, ref: false, pipeline_link: false, allow_retry: true = render stage.latest_ordered_statuses, stage: false, ref: false, pipeline_link: false, allow_retry: true
= render stage.statuses.retried_ordered, stage: false, ref: false, pipeline_link: false, retried: true = render stage.retried_ordered_statuses, stage: false, ref: false, pipeline_link: false, retried: true
%tr %tr
%td{ colspan: 10 } %td{ colspan: 10 }
&nbsp; &nbsp;
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
.form-group.col-md-6 .form-group.col-md-6
= f.label :expires_at, _('Expires at'), class: 'label-bold' = f.label :expires_at, _('Expires at'), class: 'label-bold'
.input-icon-wrapper .input-icon-wrapper
= render_if_exists 'personal_access_tokens/callout_max_personal_access_token_lifetime'
= f.text_field :expires_at, class: "datepicker form-control", placeholder: 'YYYY-MM-DD' = f.text_field :expires_at, class: "datepicker form-control", placeholder: 'YYYY-MM-DD'
.form-group .form-group
......
...@@ -4,16 +4,13 @@ ...@@ -4,16 +4,13 @@
- breadcrumb_title @snippet.to_reference - breadcrumb_title @snippet.to_reference
- page_title "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets") - page_title "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets")
- if Feature.enabled?(:snippets_vue) = render 'shared/snippets/header'
#js-snippet-view{ 'data-qa-selector': 'snippet_view' }
- else
= render 'shared/snippets/header'
.personal-snippets .personal-snippets
%article.file-holder.snippet-file-content %article.file-holder.snippet-file-content
= render 'shared/snippets/blob' = render 'shared/snippets/blob'
.row-content-block.top-block.content-component-block .row-content-block.top-block.content-component-block
= render 'award_emoji/awards_block', awardable: @snippet, inline: true = render 'award_emoji/awards_block', awardable: @snippet, inline: true
#notes.limited-width-notes= render "shared/notes/notes_with_form", :autocomplete => false #notes.limited-width-notes= render "shared/notes/notes_with_form", :autocomplete => false
---
title: Preserve merge train history
merge_request: 19864
author:
type: changed
...@@ -123,3 +123,5 @@ ...@@ -123,3 +123,5 @@
- [refresh_license_compliance_checks, 2] - [refresh_license_compliance_checks, 2]
- [design_management_new_version, 1] - [design_management_new_version, 1]
- [epics, 2] - [epics, 2]
- [personal_access_tokens, 1]
# frozen_string_literal: true
class AddMaxPersonalAccessTokenLifetimeToApplicationSettings < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :application_settings, :max_personal_access_token_lifetime, :integer
end
end
# frozen_string_literal: true
class AddStateToMergeTrains < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
MERGE_TRAIN_STATUS_CREATED = 0 # Equivalent to MergeTrain.statuses[:created]
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default :merge_trains, :status, :integer, limit: 2, default: MERGE_TRAIN_STATUS_CREATED
end
def down
remove_column :merge_trains, :status
end
end
# frozen_string_literal: true
class AddIndexOnPersonalAccessTokensUserIdAndExpiresAt < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'index_pat_on_user_id_and_expires_at'
disable_ddl_transaction!
def up
add_concurrent_index :personal_access_tokens, [:user_id, :expires_at], name: INDEX_NAME, using: :btree
end
def down
remove_concurrent_index_by_name :personal_access_tokens, INDEX_NAME
end
end
# frozen_string_literal: true
class AddIndexOnStatusToMergeTrains < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'index_for_status_per_branch_per_project'
disable_ddl_transaction!
def up
add_concurrent_index :merge_trains, [:target_project_id, :target_branch, :status], name: INDEX_NAME
remove_concurrent_index :merge_trains, :target_project_id
end
def down
add_concurrent_index :merge_trains, :target_project_id
remove_concurrent_index :merge_trains, [:target_project_id, :target_branch, :status], name: INDEX_NAME
end
end
...@@ -325,6 +325,7 @@ ActiveRecord::Schema.define(version: 2019_12_02_031812) do ...@@ -325,6 +325,7 @@ ActiveRecord::Schema.define(version: 2019_12_02_031812) do
t.string "encrypted_asset_proxy_secret_key_iv" t.string "encrypted_asset_proxy_secret_key_iv"
t.string "static_objects_external_storage_url", limit: 255 t.string "static_objects_external_storage_url", limit: 255
t.string "static_objects_external_storage_auth_token", limit: 255 t.string "static_objects_external_storage_auth_token", limit: 255
t.integer "max_personal_access_token_lifetime"
t.boolean "throttle_protected_paths_enabled", default: false, null: false t.boolean "throttle_protected_paths_enabled", default: false, null: false
t.integer "throttle_protected_paths_requests_per_period", default: 10, null: false t.integer "throttle_protected_paths_requests_per_period", default: 10, null: false
t.integer "throttle_protected_paths_period_in_seconds", default: 60, null: false t.integer "throttle_protected_paths_period_in_seconds", default: 60, null: false
...@@ -2524,9 +2525,10 @@ ActiveRecord::Schema.define(version: 2019_12_02_031812) do ...@@ -2524,9 +2525,10 @@ ActiveRecord::Schema.define(version: 2019_12_02_031812) do
t.datetime_with_timezone "updated_at", null: false t.datetime_with_timezone "updated_at", null: false
t.integer "target_project_id", null: false t.integer "target_project_id", null: false
t.text "target_branch", null: false t.text "target_branch", null: false
t.integer "status", limit: 2, default: 0, null: false
t.index ["merge_request_id"], name: "index_merge_trains_on_merge_request_id", unique: true t.index ["merge_request_id"], name: "index_merge_trains_on_merge_request_id", unique: true
t.index ["pipeline_id"], name: "index_merge_trains_on_pipeline_id" t.index ["pipeline_id"], name: "index_merge_trains_on_pipeline_id"
t.index ["target_project_id"], name: "index_merge_trains_on_target_project_id" t.index ["target_project_id", "target_branch", "status"], name: "index_for_status_per_branch_per_project"
t.index ["user_id"], name: "index_merge_trains_on_user_id" t.index ["user_id"], name: "index_merge_trains_on_user_id"
end end
...@@ -2925,6 +2927,7 @@ ActiveRecord::Schema.define(version: 2019_12_02_031812) do ...@@ -2925,6 +2927,7 @@ ActiveRecord::Schema.define(version: 2019_12_02_031812) do
t.boolean "impersonation", default: false, null: false t.boolean "impersonation", default: false, null: false
t.string "token_digest" t.string "token_digest"
t.index ["token_digest"], name: "index_personal_access_tokens_on_token_digest", unique: true t.index ["token_digest"], name: "index_personal_access_tokens_on_token_digest", unique: true
t.index ["user_id", "expires_at"], name: "index_pat_on_user_id_and_expires_at"
t.index ["user_id"], name: "index_personal_access_tokens_on_user_id" t.index ["user_id"], name: "index_personal_access_tokens_on_user_id"
end end
......
...@@ -269,6 +269,7 @@ are listed in the descriptions of the relevant settings. ...@@ -269,6 +269,7 @@ are listed in the descriptions of the relevant settings.
| `max_artifacts_size` | integer | no | Maximum artifacts size in MB | | `max_artifacts_size` | integer | no | Maximum artifacts size in MB |
| `max_attachment_size` | integer | no | Limit attachment size in MB | | `max_attachment_size` | integer | no | Limit attachment size in MB |
| `max_pages_size` | integer | no | Maximum size of pages repositories in MB | | `max_pages_size` | integer | no | Maximum size of pages repositories in MB |
| `max_personal_access_token_lifetime` | integer | no | **(ULTIMATE ONLY)** Maximum allowable lifetime for personal access tokens in days |
| `metrics_enabled` | boolean | no | (**If enabled, requires:** `metrics_host`, `metrics_method_call_threshold`, `metrics_packet_size`, `metrics_pool_size`, `metrics_port`, `metrics_sample_interval` and `metrics_timeout`) Enable influxDB metrics. | | `metrics_enabled` | boolean | no | (**If enabled, requires:** `metrics_host`, `metrics_method_call_threshold`, `metrics_packet_size`, `metrics_pool_size`, `metrics_port`, `metrics_sample_interval` and `metrics_timeout`) Enable influxDB metrics. |
| `metrics_host` | string | required by: `metrics_enabled` | InfluxDB host. | | `metrics_host` | string | required by: `metrics_enabled` | InfluxDB host. |
| `metrics_method_call_threshold` | integer | required by: `metrics_enabled` | A method call is only tracked when it takes longer than the given amount of milliseconds. | | `metrics_method_call_threshold` | integer | required by: `metrics_enabled` | A method call is only tracked when it takes longer than the given amount of milliseconds. |
......
...@@ -303,6 +303,41 @@ You are not required to use the same prefix or only slashes (`/`) in the dynamic ...@@ -303,6 +303,41 @@ You are not required to use the same prefix or only slashes (`/`) in the dynamic
names. However, using this format will enable the [grouping similar environments](#grouping-similar-environments) names. However, using this format will enable the [grouping similar environments](#grouping-similar-environments)
feature. feature.
### Configuring Kubernetes deployments
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/27630) in GitLab 12.6.
If you are deploying to a [Kubernetes cluster](../user/project/clusters/index.md)
associated with your project, you can configure these deployments from your
`gitlab-ci.yml` file.
The following configuration options are supported:
- [`namespace`](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/)
In the following example, the job will deploy your application to the
`production` Kubernetes namespace.
```yaml
deploy:
stage: deploy
script:
- echo "Deploy to production server"
environment:
name: production
url: https://example.com
kubernetes:
namespace: production
only:
- master
```
NOTE: **Note:**
Kubernetes configuration is not supported for Kubernetes clusters
that are [managed by GitLab](../user/project/clusters/index.md#gitlab-managed-clusters).
To follow progress on support for Gitlab-managed clusters, see the
[relevant issue](https://gitlab.com/gitlab-org/gitlab/issues/38054).
### Complete example ### Complete example
The configuration in this section provides a full development workflow where your app is: The configuration in this section provides a full development workflow where your app is:
......
...@@ -1421,6 +1421,38 @@ The `stop_review_app` job is **required** to have the following keywords defined ...@@ -1421,6 +1421,38 @@ The `stop_review_app` job is **required** to have the following keywords defined
- `stage` should be the same as the `review_app` in order for the environment - `stage` should be the same as the `review_app` in order for the environment
to stop automatically when the branch is deleted to stop automatically when the branch is deleted
#### `environment:kubernetes`
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/27630) in GitLab 12.6.
The `kubernetes` block is used to configure deployments to a
[Kubernetes cluster](../../user/project/clusters/index.md) that is associated with your project.
For example:
```yaml
deploy:
stage: deploy
script: make deploy-app
environment:
name: production
kubernetes:
namespace: production
```
This will set up the `deploy` job to deploy to the `production`
environment, using the `production`
[Kubernetes namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/).
For more information, see
[Available settings for `kubernetes`](../environments.md#configuring-kubernetes-deployments).
NOTE: **Note:**
Kubernetes configuration is not supported for Kubernetes clusters
that are [managed by GitLab](../../user/project/clusters/index.md#gitlab-managed-clusters).
To follow progress on support for Gitlab-managed clusters, see the
[relevant issue](https://gitlab.com/gitlab-org/gitlab/issues/38054).
#### Dynamic environments #### Dynamic environments
> - [Introduced][ce-6323] in GitLab 8.12 and GitLab Runner 1.6. > - [Introduced][ce-6323] in GitLab 8.12 and GitLab Runner 1.6.
......
...@@ -84,3 +84,35 @@ add the line below to `/etc/gitlab/gitlab.rb` before increasing the max attachme ...@@ -84,3 +84,35 @@ add the line below to `/etc/gitlab/gitlab.rb` before increasing the max attachme
``` ```
nginx['client_max_body_size'] = "200m" nginx['client_max_body_size'] = "200m"
``` ```
## Limiting lifetime of personal access tokens **(ULTIMATE ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/3649) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.6.
Users can optionally specify an expiration date for
[personal access tokens](../../profile/personal_access_tokens.md).
This expiration date is not a requirement, and can be set to any arbitrary date.
Since personal access tokens are the only token needed for programmatic access to GitLab,
organizations with security requirements may want to enforce more protection to require
regular rotation of these tokens.
### Setting a limit
Only a GitLab administrator can set a limit. Leaving it empty means
there are no restrictions.
To set a limit on how long personal access tokens are valid:
1. Navigate to **Admin Area > Settings > General**.
1. Expand the **Account and limit** section.
1. Fill in the **Maximun allowable lifetime for personal access tokens (days)** field.
1. Click **Save changes**.
Once a lifetime for personal access tokens is set, GitLab will:
- Apply the lifetime for new personal access tokens, and require users to set an expiration date
and a date no later than the allowed lifetime.
- After three hours, revoke old tokens with no expiration date or with a lifetime longer than the
allowed lifetime. Three hours is given to allow administrators to change the allowed lifetime,
or remove it, before revocation takes place.
...@@ -4,45 +4,6 @@ module Gitlab ...@@ -4,45 +4,6 @@ module Gitlab
module Diff module Diff
module FileCollection module FileCollection
class MergeRequestDiff < MergeRequestDiffBase class MergeRequestDiff < MergeRequestDiffBase
include Gitlab::Utils::StrongMemoize
def diff_files
strong_memoize(:diff_files) do
diff_files = super
diff_files.each { |diff_file| cache.decorate(diff_file) }
diff_files
end
end
override :write_cache
def write_cache
cache.write_if_empty
end
override :clear_cache
def clear_cache
cache.clear
end
def cache_key
cache.key
end
def real_size
@merge_request_diff.real_size
end
private
def cache
@cache ||= if Feature.enabled?(:hset_redis_diff_caching, project)
Gitlab::Diff::HighlightCache.new(self)
else
Gitlab::Diff::DeprecatedHighlightCache.new(self)
end
end
end end
end end
end end
......
...@@ -15,6 +15,44 @@ module Gitlab ...@@ -15,6 +15,44 @@ module Gitlab
diff_refs: merge_request_diff.diff_refs, diff_refs: merge_request_diff.diff_refs,
fallback_diff_refs: merge_request_diff.fallback_diff_refs) fallback_diff_refs: merge_request_diff.fallback_diff_refs)
end end
def diff_files
strong_memoize(:diff_files) do
diff_files = super
diff_files.each { |diff_file| cache.decorate(diff_file) }
diff_files
end
end
override :write_cache
def write_cache
cache.write_if_empty
end
override :clear_cache
def clear_cache
cache.clear
end
def cache_key
cache.key
end
def real_size
@merge_request_diff.real_size
end
private
def cache
@cache ||= if Feature.enabled?(:hset_redis_diff_caching, project)
Gitlab::Diff::HighlightCache.new(self)
else
Gitlab::Diff::DeprecatedHighlightCache.new(self)
end
end
end end
end end
end end
......
...@@ -42,7 +42,6 @@ module Gitlab ...@@ -42,7 +42,6 @@ module Gitlab
# Initialize gon.features with any flags that should be # Initialize gon.features with any flags that should be
# made globally available to the frontend # made globally available to the frontend
push_frontend_feature_flag(:suppress_ajax_navigation_errors, default_enabled: true) push_frontend_feature_flag(:suppress_ajax_navigation_errors, default_enabled: true)
push_frontend_feature_flag(:snippets_vue, default_enabled: false)
end end
# Exposes the state of a feature flag to the frontend code. # Exposes the state of a feature flag to the frontend code.
......
...@@ -9128,6 +9128,9 @@ msgstr "" ...@@ -9128,6 +9128,9 @@ msgstr ""
msgid "Helps reduce request volume for protected paths" msgid "Helps reduce request volume for protected paths"
msgstr "" msgstr ""
msgid "Hi %{username}!"
msgstr ""
msgid "Hide archived projects" msgid "Hide archived projects"
msgstr "" msgstr ""
...@@ -10274,6 +10277,9 @@ msgstr "" ...@@ -10274,6 +10277,9 @@ msgstr ""
msgid "Leave Admin Mode" msgid "Leave Admin Mode"
msgstr "" msgstr ""
msgid "Leave blank for no limit. Once set, existing personal access tokens may be revoked"
msgstr ""
msgid "Leave edit mode? All unsaved changes will be lost." msgid "Leave edit mode? All unsaved changes will be lost."
msgstr "" msgstr ""
...@@ -10764,6 +10770,9 @@ msgstr "" ...@@ -10764,6 +10770,9 @@ msgstr ""
msgid "Max seats used" msgid "Max seats used"
msgstr "" msgstr ""
msgid "Maximum allowable lifetime for personal access token (days)"
msgstr ""
msgid "Maximum artifacts size (MB)" msgid "Maximum artifacts size (MB)"
msgstr "" msgstr ""
...@@ -10782,6 +10791,9 @@ msgstr "" ...@@ -10782,6 +10791,9 @@ msgstr ""
msgid "Maximum job timeout has a value which could not be accepted" msgid "Maximum job timeout has a value which could not be accepted"
msgstr "" msgstr ""
msgid "Maximum lifetime allowable for Personal Access Tokens is active, your expire date must be set before %{maximum_allowable_date}."
msgstr ""
msgid "Maximum number of comments exceeded" msgid "Maximum number of comments exceeded"
msgstr "" msgstr ""
...@@ -11985,6 +11997,9 @@ msgstr[1] "" ...@@ -11985,6 +11997,9 @@ msgstr[1] ""
msgid "One or more groups that you don't have access to." msgid "One or more groups that you don't have access to."
msgstr "" msgstr ""
msgid "One or more of you personal access tokens were revoked"
msgstr ""
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git." msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr "" msgstr ""
...@@ -17439,6 +17454,11 @@ msgstr "" ...@@ -17439,6 +17454,11 @@ msgstr ""
msgid "The following items will be exported:" msgid "The following items will be exported:"
msgstr "" msgstr ""
msgid "The following personal access token: %{token_names} was revoked, because a new policy to expire personal access tokens were set."
msgid_plural "The following personal access tokens: %{token_names} were revoked, because a new policy to expire personal access tokens were set."
msgstr[0] ""
msgstr[1] ""
msgid "The fork relationship has been removed." msgid "The fork relationship has been removed."
msgstr "" msgstr ""
...@@ -20114,6 +20134,12 @@ msgstr "" ...@@ -20114,6 +20134,12 @@ msgstr ""
msgid "You can create files directly in GitLab using one of the following options." msgid "You can create files directly in GitLab using one of the following options."
msgstr "" msgstr ""
msgid "You can create new ones at your %{pat_link_start}Personal Access Tokens%{pat_link_end} settings"
msgstr ""
msgid "You can create new ones at your Personal Access Tokens settings %{pat_link}"
msgstr ""
msgid "You can easily contribute to them by requesting to join these groups." msgid "You can easily contribute to them by requesting to join these groups."
msgstr "" msgstr ""
......
...@@ -25,6 +25,16 @@ describe Projects::MergeRequests::DiffsController do ...@@ -25,6 +25,16 @@ describe Projects::MergeRequests::DiffsController do
end end
end end
shared_examples 'cached diff collection' do
it 'ensures diff highlighting cache writing' do
expect_next_instance_of(Gitlab::Diff::HighlightCache) do |cache|
expect(cache).to receive(:write_if_empty).once
end
go
end
end
shared_examples 'persisted preferred diff view cookie' do shared_examples 'persisted preferred diff view cookie' do
context 'with view param' do context 'with view param' do
before do before do
...@@ -112,6 +122,7 @@ describe Projects::MergeRequests::DiffsController do ...@@ -112,6 +122,7 @@ describe Projects::MergeRequests::DiffsController do
end end
it_behaves_like 'persisted preferred diff view cookie' it_behaves_like 'persisted preferred diff view cookie'
it_behaves_like 'cached diff collection'
end end
describe 'GET diffs_metadata' do describe 'GET diffs_metadata' do
...@@ -404,6 +415,7 @@ describe Projects::MergeRequests::DiffsController do ...@@ -404,6 +415,7 @@ describe Projects::MergeRequests::DiffsController do
it_behaves_like 'forked project with submodules' it_behaves_like 'forked project with submodules'
it_behaves_like 'persisted preferred diff view cookie' it_behaves_like 'persisted preferred diff view cookie'
it_behaves_like 'cached diff collection'
context 'diff unfolding' do context 'diff unfolding' do
let!(:unfoldable_diff_note) do let!(:unfoldable_diff_note) do
......
...@@ -5,10 +5,6 @@ require 'spec_helper' ...@@ -5,10 +5,6 @@ require 'spec_helper'
describe 'Internal Snippets', :js do describe 'Internal Snippets', :js do
let(:internal_snippet) { create(:personal_snippet, :internal) } let(:internal_snippet) { create(:personal_snippet, :internal) }
before do
stub_feature_flags(snippets_vue: false)
end
describe 'normal user' do describe 'normal user' do
before do before do
sign_in(create(:user)) sign_in(create(:user))
......
...@@ -16,7 +16,6 @@ describe 'Comments on personal snippets', :js do ...@@ -16,7 +16,6 @@ describe 'Comments on personal snippets', :js do
let!(:other_note) { create(:note_on_personal_snippet) } let!(:other_note) { create(:note_on_personal_snippet) }
before do before do
stub_feature_flags(snippets_vue: false)
sign_in user sign_in user
visit snippet_path(snippet) visit snippet_path(snippet)
......
...@@ -6,7 +6,6 @@ describe 'Private Snippets', :js do ...@@ -6,7 +6,6 @@ describe 'Private Snippets', :js do
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
stub_feature_flags(snippets_vue: false)
sign_in(user) sign_in(user)
end end
......
...@@ -3,10 +3,6 @@ ...@@ -3,10 +3,6 @@
require 'spec_helper' require 'spec_helper'
describe 'Public Snippets', :js do describe 'Public Snippets', :js do
before do
stub_feature_flags(snippets_vue: false)
end
it 'Unauthenticated user should see public snippets' do it 'Unauthenticated user should see public snippets' do
public_snippet = create(:personal_snippet, :public) public_snippet = create(:personal_snippet, :public)
......
...@@ -6,10 +6,6 @@ describe 'Snippet', :js do ...@@ -6,10 +6,6 @@ describe 'Snippet', :js do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content) } let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content) }
before do
stub_feature_flags(snippets_vue: false)
end
context 'Ruby file' do context 'Ruby file' do
let(:file_name) { 'popen.rb' } let(:file_name) { 'popen.rb' }
let(:content) { project.repository.blob_at('master', 'files/ruby/popen.rb').data } let(:content) { project.repository.blob_at('master', 'files/ruby/popen.rb').data }
......
...@@ -7,7 +7,6 @@ describe 'User creates snippet', :js do ...@@ -7,7 +7,6 @@ describe 'User creates snippet', :js do
before do before do
stub_feature_flags(allow_possible_spam: false) stub_feature_flags(allow_possible_spam: false)
stub_feature_flags(snippets_vue: false)
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
Gitlab::CurrentSettings.update!( Gitlab::CurrentSettings.update!(
......
...@@ -8,7 +8,6 @@ describe 'User creates snippet', :js do ...@@ -8,7 +8,6 @@ describe 'User creates snippet', :js do
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
stub_feature_flags(snippets_vue: false)
sign_in(user) sign_in(user)
visit new_snippet_path visit new_snippet_path
end end
......
...@@ -10,8 +10,6 @@ describe 'User deletes snippet' do ...@@ -10,8 +10,6 @@ describe 'User deletes snippet' do
before do before do
sign_in(user) sign_in(user)
stub_feature_flags(snippets_vue: false)
visit snippet_path(snippet) visit snippet_path(snippet)
end end
......
...@@ -12,7 +12,6 @@ describe 'User edits snippet', :js do ...@@ -12,7 +12,6 @@ describe 'User edits snippet', :js do
let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, author: user) } let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, author: user) }
before do before do
stub_feature_flags(snippets_vue: false)
sign_in(user) sign_in(user)
visit edit_snippet_path(snippet) visit edit_snippet_path(snippet)
......
...@@ -6,38 +6,11 @@ describe 'Snippets' do ...@@ -6,38 +6,11 @@ describe 'Snippets' do
context 'when the project has snippets' do context 'when the project has snippets' do
let(:project) { create(:project, :public) } let(:project) { create(:project, :public) }
let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) } let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) }
before do before do
allow(Snippet).to receive(:default_per_page).and_return(1) allow(Snippet).to receive(:default_per_page).and_return(1)
visit snippets_path(username: project.owner.username)
visit project_snippets_path(project)
end end
it_behaves_like 'paginated snippets' it_behaves_like 'paginated snippets'
end end
describe 'rendering engine' do
let_it_be(:snippet) { create(:personal_snippet, :public) }
let(:snippets_vue_feature_flag_enabled) { true }
before do
stub_feature_flags(snippets_vue: snippets_vue_feature_flag_enabled)
visit snippet_path(snippet)
end
it 'renders Vue application' do
expect(page).to have_selector('#js-snippet-view')
expect(page).not_to have_selector('.personal-snippets')
end
context 'when feature flag is disabled' do
let(:snippets_vue_feature_flag_enabled) { false }
it 'renders HAML application and not Vue' do
expect(page).not_to have_selector('#js-snippet-view')
expect(page).to have_selector('.personal-snippets')
end
end
end
end end
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
import Embed from '~/monitoring/components/embed.vue'; import Embed from '~/monitoring/components/embed.vue';
import MonitorTimeSeriesChart from '~/monitoring/components/charts/time_series.vue'; import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import { groups, initialState, metricsData, metricsWithData } from './mock_data'; import { groups, initialState, metricsData, metricsWithData } from './mock_data';
...@@ -55,7 +55,7 @@ describe('Embed', () => { ...@@ -55,7 +55,7 @@ describe('Embed', () => {
it('shows an empty state when no metrics are present', () => { it('shows an empty state when no metrics are present', () => {
expect(wrapper.find('.metrics-embed').exists()).toBe(true); expect(wrapper.find('.metrics-embed').exists()).toBe(true);
expect(wrapper.find(MonitorTimeSeriesChart).exists()).toBe(false); expect(wrapper.find(PanelType).exists()).toBe(false);
}); });
}); });
...@@ -71,12 +71,12 @@ describe('Embed', () => { ...@@ -71,12 +71,12 @@ describe('Embed', () => {
it('shows a chart when metrics are present', () => { it('shows a chart when metrics are present', () => {
wrapper.setProps({}); wrapper.setProps({});
expect(wrapper.find('.metrics-embed').exists()).toBe(true); expect(wrapper.find('.metrics-embed').exists()).toBe(true);
expect(wrapper.find(MonitorTimeSeriesChart).exists()).toBe(true); expect(wrapper.find(PanelType).exists()).toBe(true);
expect(wrapper.findAll(MonitorTimeSeriesChart).length).toBe(2); expect(wrapper.findAll(PanelType).length).toBe(2);
}); });
it('includes groupId with dashboardUrl', () => { it('includes groupId with dashboardUrl', () => {
expect(wrapper.find(MonitorTimeSeriesChart).props('groupId')).toBe(TEST_HOST); expect(wrapper.find(PanelType).props('groupId')).toBe(TEST_HOST);
}); });
}); });
}); });
...@@ -102,6 +102,10 @@ describe('Panel Type component', () => { ...@@ -102,6 +102,10 @@ describe('Panel Type component', () => {
expect(clipboardText()).toBe(exampleText); expect(clipboardText()).toBe(exampleText);
}); });
it('includes a default group id', () => {
expect(panelType.vm.groupId).toBe('panel-type-chart');
});
}); });
describe('Anomaly Chart panel type', () => { describe('Anomaly Chart panel type', () => {
......
...@@ -123,4 +123,8 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiffBatch do ...@@ -123,4 +123,8 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiffBatch do
collection_default_args) collection_default_args)
end end
end end
it_behaves_like 'cacheable diff collection' do
let(:cacheable_files_count) { batch_size }
end
end end
...@@ -4,7 +4,8 @@ require 'spec_helper' ...@@ -4,7 +4,8 @@ require 'spec_helper'
describe Gitlab::Diff::FileCollection::MergeRequestDiff do describe Gitlab::Diff::FileCollection::MergeRequestDiff do
let(:merge_request) { create(:merge_request) } let(:merge_request) { create(:merge_request) }
let(:subject) { described_class.new(merge_request.merge_request_diff, diff_options: nil) } let(:diffable) { merge_request.merge_request_diff }
let(:subject) { described_class.new(diffable, diff_options: nil) }
let(:diff_files) { subject.diff_files } let(:diff_files) { subject.diff_files }
describe '#diff_files' do describe '#diff_files' do
...@@ -52,6 +53,10 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiff do ...@@ -52,6 +53,10 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiff do
let(:stub_path) { '.gitignore' } let(:stub_path) { '.gitignore' }
end end
it_behaves_like 'cacheable diff collection' do
let(:cacheable_files_count) { diffable.size.to_i }
end
it 'returns a valid instance of a DiffCollection' do it 'returns a valid instance of a DiffCollection' do
expect(diff_files).to be_a(Gitlab::Git::DiffCollection) expect(diff_files).to be_a(Gitlab::Git::DiffCollection)
end end
......
# frozen_string_literal: true
require 'spec_helper'
describe Ci::LegacyStagePresenter do
let(:legacy_stage) { create(:ci_stage) }
let(:presenter) { described_class.new(legacy_stage) }
let!(:build) { create(:ci_build, :tags, pipeline: legacy_stage.pipeline, stage: legacy_stage.name) }
let!(:retried_build) { create(:ci_build, :tags, :retried, pipeline: legacy_stage.pipeline, stage: legacy_stage.name) }
before do
create(:generic_commit_status, pipeline: legacy_stage.pipeline, stage: legacy_stage.name)
end
describe '#latest_ordered_statuses' do
subject(:latest_ordered_statuses) { presenter.latest_ordered_statuses }
it 'preloads build tags' do
expect(latest_ordered_statuses.second.association(:tags)).to be_loaded
end
end
describe '#retried_ordered_statuses' do
subject(:retried_ordered_statuses) { presenter.retried_ordered_statuses }
it 'preloads build tags' do
expect(retried_ordered_statuses.first.association(:tags)).to be_loaded
end
end
end
...@@ -57,3 +57,45 @@ shared_examples 'unfoldable diff' do ...@@ -57,3 +57,45 @@ shared_examples 'unfoldable diff' do
subject.unfold_diff_files([position]) subject.unfold_diff_files([position])
end end
end end
shared_examples 'cacheable diff collection' do
let(:cache) { instance_double(Gitlab::Diff::HighlightCache) }
before do
expect(Gitlab::Diff::HighlightCache).to receive(:new).with(subject) { cache }
end
describe '#write_cache' do
it 'calls Gitlab::Diff::HighlightCache#write_if_empty' do
expect(cache).to receive(:write_if_empty).once
subject.write_cache
end
end
describe '#clear_cache' do
it 'calls Gitlab::Diff::HighlightCache#clear' do
expect(cache).to receive(:clear).once
subject.clear_cache
end
end
describe '#cache_key' do
it 'calls Gitlab::Diff::HighlightCache#key' do
expect(cache).to receive(:key).once
subject.cache_key
end
end
describe '#diff_files' do
it 'calls Gitlab::Diff::HighlightCache#decorate' do
expect(cache).to receive(:decorate)
.with(instance_of(Gitlab::Diff::File))
.exactly(cacheable_files_count).times
subject.diff_files
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment