Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-05-14 15:10:35 +00:00
parent 793d974d7c
commit 7172fb1031
37 changed files with 843 additions and 97 deletions

View File

@ -2,6 +2,15 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 13.11.4 (2021-05-14)
### Fixed (3 changes)
- Fix N+1 SQL queries in PipelinesController#show. !60794
- Omit trailing slash when proxying pre-authorized routes with no suffix. !61638
- Omit trailing slash when checking allowed requests in the read-only middleware. !61641
## 13.11.3 (2021-04-30)
### Fixed (1 change)

View File

@ -91,7 +91,9 @@ export default {
data-testid="stuck-icon"
/>
<div class="gl-display-flex gl-align-items-center">
<div
class="gl-display-flex gl-align-items-center gl-lg-justify-content-start gl-justify-content-end"
>
<div v-if="jobRef" class="gl-max-w-15 gl-text-truncate">
<gl-icon
v-if="createdByTag"

View File

@ -11,6 +11,8 @@ const defaultTableClasses = {
tdClass: 'gl-p-5!',
thClass: 'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-100! gl-p-5! gl-border-b-1!',
};
// eslint-disable-next-line @gitlab/require-i18n-strings
const coverageTdClasses = `${defaultTableClasses.tdClass} gl-display-none! gl-lg-display-table-cell!`;
export default {
i18n: {
@ -56,7 +58,8 @@ export default {
{
key: 'coverage',
label: __('Coverage'),
...defaultTableClasses,
tdClass: coverageTdClasses,
thClass: defaultTableClasses.thClass,
columnClass: 'gl-w-10p',
},
{

View File

@ -1,6 +1,7 @@
import { __ } from '~/locale';
export const DEBOUNCE_DELAY = 200;
export const MAX_RECENT_TOKENS_SIZE = 3;
export const FILTER_NONE = 'None';
export const FILTER_ANY = 'Any';

View File

@ -1,6 +1,9 @@
import { isEmpty } from 'lodash';
import { isEmpty, uniqWith, isEqual } from 'lodash';
import AccessorUtilities from '~/lib/utils/accessor';
import { queryToObject } from '~/lib/utils/url_utility';
import { MAX_RECENT_TOKENS_SIZE } from './constants';
/**
* Strips enclosing quotations from a string if it has one.
*
@ -162,3 +165,38 @@ export function urlQueryToFilter(query = '') {
return { ...memo, [filterName]: { value, operator } };
}, {});
}
/**
* Returns array of token values from localStorage
* based on provided recentTokenValuesStorageKey
*
* @param {String} recentTokenValuesStorageKey
* @returns
*/
export function getRecentlyUsedTokenValues(recentTokenValuesStorageKey) {
let recentlyUsedTokenValues = [];
if (AccessorUtilities.isLocalStorageAccessSafe()) {
recentlyUsedTokenValues = JSON.parse(localStorage.getItem(recentTokenValuesStorageKey)) || [];
}
return recentlyUsedTokenValues;
}
/**
* Sets provided token value to recently used array
* within localStorage for provided recentTokenValuesStorageKey
*
* @param {String} recentTokenValuesStorageKey
* @param {Object} tokenValue
*/
export function setTokenValueToRecentlyUsed(recentTokenValuesStorageKey, tokenValue) {
const recentlyUsedTokenValues = getRecentlyUsedTokenValues(recentTokenValuesStorageKey);
recentlyUsedTokenValues.splice(0, 0, { ...tokenValue });
if (AccessorUtilities.isLocalStorageAccessSafe()) {
localStorage.setItem(
recentTokenValuesStorageKey,
JSON.stringify(uniqWith(recentlyUsedTokenValues, isEqual).slice(0, MAX_RECENT_TOKENS_SIZE)),
);
}
}

View File

@ -0,0 +1,167 @@
<script>
import {
GlFilteredSearchToken,
GlFilteredSearchSuggestion,
GlDropdownDivider,
GlDropdownSectionHeader,
GlLoadingIcon,
} from '@gitlab/ui';
import { DEBOUNCE_DELAY } from '../constants';
import { getRecentlyUsedTokenValues, setTokenValueToRecentlyUsed } from '../filtered_search_utils';
export default {
components: {
GlFilteredSearchToken,
GlFilteredSearchSuggestion,
GlDropdownDivider,
GlDropdownSectionHeader,
GlLoadingIcon,
},
props: {
tokenConfig: {
type: Object,
required: true,
},
tokenValue: {
type: Object,
required: true,
},
tokenActive: {
type: Boolean,
required: true,
},
tokensListLoading: {
type: Boolean,
required: true,
},
tokenValues: {
type: Array,
required: true,
},
fnActiveTokenValue: {
type: Function,
required: true,
},
defaultTokenValues: {
type: Array,
required: false,
default: () => [],
},
recentTokenValuesStorageKey: {
type: String,
required: false,
default: '',
},
valueIdentifier: {
type: String,
required: false,
default: 'id',
},
fnCurrentTokenValue: {
type: Function,
required: false,
default: null,
},
},
data() {
return {
searchKey: '',
recentTokenValues: this.recentTokenValuesStorageKey
? getRecentlyUsedTokenValues(this.recentTokenValuesStorageKey)
: [],
loading: false,
};
},
computed: {
isRecentTokenValuesEnabled() {
return Boolean(this.recentTokenValuesStorageKey);
},
recentTokenIds() {
return this.recentTokenValues.map((tokenValue) => tokenValue.id || tokenValue.name);
},
currentTokenValue() {
if (this.fnCurrentTokenValue) {
return this.fnCurrentTokenValue(this.tokenValue.data);
}
return this.tokenValue.data.toLowerCase();
},
activeTokenValue() {
return this.fnActiveTokenValue(this.tokenValues, this.currentTokenValue);
},
/**
* Return all the tokenValues when searchKey is present
* otherwise return only the tokenValues which aren't
* present in "Recently used"
*/
availableTokenValues() {
return this.searchKey
? this.tokenValues
: this.tokenValues.filter(
(tokenValue) => !this.recentTokenIds.includes(tokenValue[this.valueIdentifier]),
);
},
},
watch: {
tokenActive: {
immediate: true,
handler(newValue) {
if (!newValue && !this.tokenValues.length) {
this.$emit('fetch-token-values', this.tokenValue.data);
}
},
},
},
methods: {
handleInput({ data }) {
this.searchKey = data;
setTimeout(() => {
if (!this.tokensListLoading) this.$emit('fetch-token-values', data);
}, DEBOUNCE_DELAY);
},
handleTokenValueSelected(activeTokenValue) {
if (this.isRecentTokenValuesEnabled) {
setTokenValueToRecentlyUsed(this.recentTokenValuesStorageKey, activeTokenValue);
}
},
},
};
</script>
<template>
<gl-filtered-search-token
:config="tokenConfig"
v-bind="{ ...this.$parent.$props, ...this.$parent.$attrs }"
v-on="this.$parent.$listeners"
@input="handleInput"
@select="handleTokenValueSelected(activeTokenValue)"
>
<template #view-token="viewTokenProps">
<slot name="view-token" :view-token-props="{ ...viewTokenProps, activeTokenValue }"></slot>
</template>
<template #view="viewTokenProps">
<slot name="view" :view-token-props="{ ...viewTokenProps, activeTokenValue }"></slot>
</template>
<template #suggestions>
<template v-if="defaultTokenValues.length">
<gl-filtered-search-suggestion
v-for="token in defaultTokenValues"
:key="token.value"
:value="token.value"
>
{{ token.text }}
</gl-filtered-search-suggestion>
<gl-dropdown-divider />
</template>
<template v-if="isRecentTokenValuesEnabled && recentTokenValues.length && !searchKey">
<gl-dropdown-section-header>{{ __('Recently used') }}</gl-dropdown-section-header>
<slot name="token-values-list" :token-values="recentTokenValues"></slot>
<gl-dropdown-divider />
</template>
<gl-loading-icon v-if="tokensListLoading" />
<template v-else>
<slot name="token-values-list" :token-values="availableTokenValues"></slot>
</template>
</template>
</gl-filtered-search-token>
</template>

View File

@ -148,6 +148,27 @@ module CommitsHelper
end
end
# This is used to calculate a cache key for the app/views/projects/commits/_commit.html.haml
# partial. It takes some of the same parameters as used in the partial and will hash the
# current pipeline status.
#
# This includes a keyed hash for values that can be nil, to prevent invalid cache entries
# being served if the order should change in future.
def commit_partial_cache_key(commit, ref:, merge_request:, request:)
[
commit,
commit.author,
ref,
{
merge_request: merge_request,
pipeline_status: hashed_pipeline_status(commit, ref),
xhr: request.xhr?,
controller: controller.controller_path,
path: @path # referred to in #link_to_browse_code
}
]
end
protected
# Private: Returns a link to a person. If the person has a matching user and
@ -221,4 +242,14 @@ module CommitsHelper
project_commit_path(project, commit)
end
end
private
def hashed_pipeline_status(commit, ref)
status = commit.status_for(ref)
return if status.nil?
Digest::SHA1.hexdigest(status.to_s)
end
end

View File

@ -18,7 +18,7 @@ module NotifyHelper
when "Developer"
s_("InviteEmail|As a developer, you have full access to projects, so you can take an idea from concept to production.")
when "Maintainer"
s_("InviteEmail|As a maintainer, you have full access to projects. You can push commits to master and deploy to production.")
s_("InviteEmail|As a maintainer, you have full access to projects. You can push commits to the default branch and deploy to production.")
when "Owner"
s_("InviteEmail|As an owner, you have full access to projects and can manage access to the group, including inviting new members.")
when "Minimal Access"

View File

@ -2539,7 +2539,7 @@ class Project < ApplicationRecord
def default_branch_or_main
return default_branch if default_branch
Gitlab::DefaultBranch.value(project: self)
Gitlab::DefaultBranch.value(object: self)
end
def ci_config_path_or_default

View File

@ -315,7 +315,7 @@ class Snippet < ApplicationRecord
override :default_branch
def default_branch
super || Gitlab::DefaultBranch.value(project: project)
super || Gitlab::DefaultBranch.value(object: project)
end
def repository_storage

View File

@ -1,12 +1,12 @@
= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-default-branch-name'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
- fallback_branch_name = '<code>master</code>'
- fallback_branch_name = "<code>#{Gitlab::DefaultBranch.value}</code>"
%fieldset
.form-group
= f.label :default_branch_name, _('Default initial branch name'), class: 'label-light'
= f.text_field :default_branch_name, placeholder: 'master', class: 'form-control gl-form-input'
= f.text_field :default_branch_name, placeholder: Gitlab::DefaultBranch.value, class: 'form-control gl-form-input'
%span.form-text.text-muted
= (_("Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used.") % { branch_name_default: fallback_branch_name } ).html_safe

View File

@ -9,12 +9,12 @@
.settings-content
= form_for @group, url: group_path(@group, anchor: 'js-default-branch-name'), html: { class: 'fieldset-form' } do |f|
= form_errors(@group)
- fallback_branch_name = '<code>master</code>'
- fallback_branch_name = "<code>#{Gitlab::DefaultBranch.value(object: @group)}</code>"
%fieldset
.form-group
= f.label :default_branch_name, _('Default initial branch name'), class: 'label-light'
= f.text_field :default_branch_name, value: group.namespace_settings&.default_branch_name, placeholder: 'master', class: 'form-control'
= f.text_field :default_branch_name, value: group.namespace_settings&.default_branch_name, placeholder: Gitlab::DefaultBranch.value(object: @group), class: 'form-control'
%span.form-text.text-muted
= (_("Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used.") % { branch_name_default: fallback_branch_name }).html_safe

View File

@ -3,22 +3,24 @@
- `assets/javascripts/diffs/components/commit_item.vue`
EXCEPTION WARNING - see above `.vue` file for de-sync drift
WARNING: When introducing new content here, please consider what
changes may need to be made in the cache keys used to
wrap this view, found in
CommitsHelper#commit_partial_cache_key
-#-----------------------------------------------------------------
- view_details = local_assigns.fetch(:view_details, false)
- merge_request = local_assigns.fetch(:merge_request, nil)
- project = local_assigns.fetch(:project) { merge_request&.project }
- ref = local_assigns.fetch(:ref) { merge_request&.source_branch }
- commit = commit.present(current_user: current_user)
- commit_status = commit.status_for(ref)
- collapsible = local_assigns.fetch(:collapsible, true)
- link_data_attrs = local_assigns.fetch(:link_data_attrs, {})
- link = commit_path(project, commit, merge_request: merge_request)
- view_details = local_assigns.fetch(:view_details, false)
- merge_request = local_assigns.fetch(:merge_request, nil)
- project = local_assigns.fetch(:project) { merge_request&.project }
- ref = local_assigns.fetch(:ref) { merge_request&.source_branch }
- commit = commit.present(current_user: current_user)
- commit_status = commit.status_for(ref)
- collapsible = local_assigns.fetch(:collapsible, true)
- link_data_attrs = local_assigns.fetch(:link_data_attrs, {})
- link = commit_path(project, commit, merge_request: merge_request)
- show_project_name = local_assigns.fetch(:show_project_name, false)
%li{ class: ["commit flex-row", ("js-toggle-container" if collapsible)], id: "commit-#{commit.short_id}" }
.avatar-cell.d-none.d-sm-block
= author_avatar(commit, size: 40, has_tooltip: false)

View File

@ -3,8 +3,8 @@
- ref = local_assigns.fetch(:ref) { merge_request&.source_branch }
- can_update_merge_request = can?(current_user, :update_merge_request, @merge_request)
- commits = @commits
- context_commits = @context_commits
- commits = @commits&.map { |commit| commit.present(current_user: current_user) }
- context_commits = @context_commits&.map { |commit| commit.present(current_user: current_user) }
- hidden = @hidden_commit_count
- commits.chunk { |c| c.committed_date.in_time_zone.to_date }.each do |day, daily_commits|
@ -14,7 +14,10 @@
%li.commits-row{ data: { day: day } }
%ul.content-list.commit-list.flex-list
= render partial: 'projects/commits/commit', collection: daily_commits, locals: { project: project, ref: ref, merge_request: merge_request }
- if Feature.enabled?(:cached_commits, project, default_enabled: :yaml)
= render partial: 'projects/commits/commit', collection: daily_commits, locals: { project: project, ref: ref, merge_request: merge_request }, cached: -> (commit) { commit_partial_cache_key(commit, ref: ref, merge_request: merge_request, request: request) }
- else
= render partial: 'projects/commits/commit', collection: daily_commits, locals: { project: project, ref: ref, merge_request: merge_request }
- if context_commits.present?
%li.commit-header.js-commit-header
@ -25,7 +28,10 @@
%li.commits-row
%ul.content-list.commit-list.flex-list
= render partial: 'projects/commits/commit', collection: context_commits, locals: { project: project, ref: ref, merge_request: merge_request }
- if Feature.enabled?(:cached_commits, project, default_enabled: :yaml)
= render partial: 'projects/commits/commit', collection: context_commits, locals: { project: project, ref: ref, merge_request: merge_request }, cached: -> (commit) { commit_partial_cache_key(commit, ref: ref, merge_request: merge_request, request: request) }
- else
= render partial: 'projects/commits/commit', collection: context_commits, locals: { project: project, ref: ref, merge_request: merge_request }
- if hidden > 0
%li.gl-alert.gl-alert-warning

View File

@ -4,11 +4,11 @@
%h3.page-title
= _("Compare Git revisions")
.sub-header-block
- example_master = capture do
%code.ref-name master
- example_branch = capture do
%code.ref-name= @project.default_branch_or_main
- example_sha = capture do
%code.ref-name 4eedf23
= html_escape(_("Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request.")) % { master: example_master.html_safe, sha: example_sha.html_safe }
= html_escape(_("Choose a branch/tag (e.g. %{branch}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request.")) % { branch: example_branch.html_safe, sha: example_sha.html_safe }
%br
= html_escape(_("Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision.")) % { b_open: '<b>'.html_safe, b_close: '</b>'.html_safe }

View File

@ -1,5 +0,0 @@
---
title: Omit trailing slash when checking allowed requests in the read-only middleware
merge_request: 61641
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Rename master to main in views placeholders
merge_request: 61252
author:
type: changed

View File

@ -1,5 +0,0 @@
---
title: Omit trailing slash when proxying pre-authorized routes with no suffix
merge_request: 61638
author:
type: fixed

View File

@ -1,5 +0,0 @@
---
title: Fix N+1 SQL queries in PipelinesController#show
merge_request: 60794
author:
type: fixed

View File

@ -0,0 +1,8 @@
---
name: cached_commits
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61617
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/330968
milestone: '13.12'
type: development
group: group::source code
default_enabled: false

View File

@ -1,5 +1,5 @@
const fs = require('fs');
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const CACHE_PATHS = [

View File

@ -1,8 +1,8 @@
/* eslint-disable no-inner-declarations, no-param-reassign */
const path = require('path');
const chalk = require('chalk');
const argumentsParser = require('commander');
const glob = require('glob');
const path = require('path');
const webpack = require('webpack');
const IS_EE = require('./helpers/is_ee_env');
const webpackConfig = require('./webpack.config.js');

View File

@ -1,11 +1,11 @@
const fs = require('fs');
const path = require('path');
const SOURCEGRAPH_VERSION = require('@sourcegraph/code-host-integration/package.json').version;
const CompressionPlugin = require('compression-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const glob = require('glob');
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const VUE_LOADER_VERSION = require('vue-loader/package.json').version;
const VUE_VERSION = require('vue/package.json').version;

View File

@ -63,7 +63,7 @@ except `main` and branches that start with `release/`.
### `only: variables` / `except: variables` examples
You can use `except:variables` to exclude jobs based on a commit message:
You can use [`except:variables`](../yaml/README.md#onlyvariables--exceptvariables) to exclude jobs based on a commit message:
```yaml
end-to-end:
@ -223,6 +223,48 @@ test:
- "README.md"
```
## Use predefined CI/CD variables to run jobs only in specific pipeline types
You can use [predefined CI/CD variables](../variables/predefined_variables.md) to choose
which pipeline types jobs run in, with:
- [`rules`](../yaml/README.md#rules)
- [`only:variables`](../yaml/README.md#onlyvariables--exceptvariables)
- [`except:variables`](../yaml/README.md#onlyvariables--exceptvariables)
The following table lists some of the variables that you can use, and the pipeline
types the variables can control for:
- Branch pipelines that run for Git `push` events to a branch, like new commits or tags.
- Tag pipelines that run only when a new Git tag is pushed to a branch.
- [Merge request pipelines](../merge_request_pipelines/index.md) that run for changes
to a merge request, like new commits or selecting the **Run pipeline** button
in a merge request's pipelines tab.
- [Scheduled pipelines](../pipelines/schedules.md).
| Variables | Branch | Tag | Merge request | Scheduled |
|--------------------------------------------|--------|-----|---------------|-----------|
| `CI_COMMIT_BRANCH` | Yes | | | Yes |
| `CI_COMMIT_TAG` | | Yes | | Yes, if the scheduled pipeline is configured to run on a tag. |
| `CI_PIPELINE_SOURCE = push` | Yes | Yes | | |
| `CI_PIPELINE_SOURCE = scheduled` | | | | Yes |
| `CI_PIPELINE_SOURCE = merge_request_event` | | | Yes | |
| `CI_MERGE_REQUEST_IID` | | | Yes | |
For example, to configure a job to run for merge request pipelines and scheduled pipelines,
but not branch or tag pipelines:
```yaml
job1:
script:
- echo
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_PIPELINE_SOURCE == "scheduled"
- if: $CI_PIPELINE_SOURCE == "push"
when: never
```
## Regular expressions
The `@` symbol denotes the beginning of a ref's repository path.

View File

@ -36,14 +36,14 @@ The keywords available for jobs are:
| [`coverage`](#coverage) | Code coverage settings for a given job. |
| [`dependencies`](#dependencies) | Restrict which artifacts are passed to a specific job by providing a list of jobs to fetch artifacts from. |
| [`environment`](#environment) | Name of an environment to which the job deploys. |
| [`except`](#only--except) | Control when jobs are not created. |
| [`except`](#only--except) | Control when jobs are not created. |
| [`extends`](#extends) | Configuration entries that this job inherits from. |
| [`image`](#image) | Use Docker images. |
| [`include`](#include) | Include external YAML files. |
| [`inherit`](#inherit) | Select which global defaults all jobs inherit. |
| [`interruptible`](#interruptible) | Defines if a job can be canceled when made redundant by a newer run. |
| [`needs`](#needs) | Execute jobs earlier than the stage ordering. |
| [`only`](#only--except) | Control when jobs are created. |
| [`only`](#only--except) | Control when jobs are created. |
| [`pages`](#pages) | Upload the result of a job to use with GitLab Pages. |
| [`parallel`](#parallel) | How many instances of a job should be run in parallel. |
| [`release`](#release) | Instructs the runner to generate a [release](../../user/project/releases/index.md) object. |
@ -1615,7 +1615,7 @@ Four keywords can be used with `only` and `except`:
- [`changes`](#onlychanges--exceptchanges)
- [`kubernetes`](#onlykubernetes--exceptkubernetes)
See [control jobs with `only` and `except`](../jobs/job_control.md#specify-when-jobs-run-with-only-and-except)
See [specify when jobs run with `only` and `except`](../jobs/job_control.md#specify-when-jobs-run-with-only-and-except)
for more details and examples.
#### `only:refs` / `except:refs`
@ -3632,10 +3632,10 @@ failure.
`artifacts:when` can be set to one of the following values:
1. `on_success` (default): Upload artifacts only when the job succeeds.
1. `on_failure`: Upload artifacts only when the job fails. Useful, for example, when
1. `on_failure`: Upload artifacts only when the job fails.
1. `always`: Always upload artifacts. Useful, for example, when
[uploading artifacts](../unit_test_reports.md#viewing-junit-screenshots-on-gitlab) required to
troubleshoot failing tests.
1. `always`: Always upload artifacts.
For example, to upload artifacts only when a job fails:

View File

@ -57,7 +57,7 @@ component has 2 indicators:
The calculation to a ratio then happens as follows:
```math
\frac {operations\_meeting\_apdex + (total\_operations - operations\_with_\errors)} {total\_apdex\_measurements + total\_operations}
\frac {operations\_meeting\_apdex + (total\_operations - operations\_with\_errors)} {total\_apdex\_measurements + total\_operations}
```
*Caveat:* Not all components are included, causing the

View File

@ -266,7 +266,7 @@ To remove a deprecated metric:
[fixtures](https://gitlab.com/gitlab-services/version-gitlab-com/-/blob/master/spec/support/usage_data_helpers.rb#L540)
used to test
[`UsageDataController#create`](https://gitlab.com/gitlab-services/version-gitlab-com/-/blob/3760ef28/spec/controllers/usage_data_controller_spec.rb#L75)
endpoint, and assure that test suite does not fail when metric that you wish to remove is not included into test payload.
endpoint, and assure that test suite does not fail when metric that you wish to remove is not included into test payload.
1. Create an issue in the
[GitLab Data Team project](https://gitlab.com/gitlab-data/analytics/-/issues).
@ -276,7 +276,7 @@ To remove a deprecated metric:
This step can be skipped if verification done during [deprecation process](#3-deprecate-a-metric)
reported that metric is not required by any data transformation in Snowflake data warehouse nor it is
used by any of SiSense dashboards.
1. After you verify the metric can be safely removed,
update the attributes of the metric's YAML definition:
@ -1024,7 +1024,13 @@ On GitLab.com, we have DangerBot setup to monitor Product Intelligence related f
### 10. Verify your metric
On GitLab.com, the Product Intelligence team regularly monitors Usage Ping. They may alert you that your metrics need further optimization to run quicker and with greater success. You may also use the [Usage Ping QA dashboard](https://app.periscopedata.com/app/gitlab/632033/Usage-Ping-QA) to check how well your metric performs. The dashboard allows filtering by GitLab version, by "Self-managed" & "SaaS" and shows you how many failures have occurred for each metric. Whenever you notice a high failure rate, you may re-optimize your metric.
On GitLab.com, the Product Intelligence team regularly [monitors Usage Ping](https://gitlab.com/groups/gitlab-org/-/epics/6000).
They may alert you that your metrics need further optimization to run quicker and with greater success.
The Usage Ping JSON payload for GitLab.com is shared in the
[#g_product_intelligence](https://gitlab.slack.com/archives/CL3A7GFPF) Slack channel every week.
You may also use the [Usage Ping QA dashboard](https://app.periscopedata.com/app/gitlab/632033/Usage-Ping-QA) to check how well your metric performs. The dashboard allows filtering by GitLab version, by "Self-managed" & "SaaS" and shows you how many failures have occurred for each metric. Whenever you notice a high failure rate, you may re-optimize your metric.
### Usage Ping local setup

View File

@ -0,0 +1,77 @@
# Read more about the feature here: https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html
browser_performance:
stage: performance
image: docker:19.03.12
allow_failure: true
variables:
DOCKER_TLS_CERTDIR: ""
SITESPEED_IMAGE: sitespeedio/sitespeed.io
SITESPEED_VERSION: 14.1.0
SITESPEED_OPTIONS: ''
services:
- docker:19.03.12-dind
script:
- |
if ! docker info &>/dev/null; then
if [ -z "$DOCKER_HOST" -a "$KUBERNETES_PORT" ]; then
export DOCKER_HOST='tcp://localhost:2375'
fi
fi
- export CI_ENVIRONMENT_URL=$(cat environment_url.txt)
- mkdir gitlab-exporter
# Busybox wget does not support proxied HTTPS, get the real thing.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/287611.
- (env | grep -i _proxy= 2>&1 >/dev/null) && apk --no-cache add wget
- wget -O gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/1.1.0/index.js
- mkdir sitespeed-results
- |
function propagate_env_vars() {
CURRENT_ENV=$(printenv)
for VAR_NAME; do
echo $CURRENT_ENV | grep "${VAR_NAME}=" > /dev/null && echo "--env $VAR_NAME "
done
}
- |
if [ -f .gitlab-urls.txt ]
then
sed -i -e 's@^@'"$CI_ENVIRONMENT_URL"'@' .gitlab-urls.txt
docker run \
$(propagate_env_vars \
auto_proxy \
https_proxy \
http_proxy \
no_proxy \
AUTO_PROXY \
HTTPS_PROXY \
HTTP_PROXY \
NO_PROXY \
) \
--shm-size=1g --rm -v "$(pwd)":/sitespeed.io $SITESPEED_IMAGE:$SITESPEED_VERSION --plugins.add ./gitlab-exporter --cpu --outputFolder sitespeed-results .gitlab-urls.txt $SITESPEED_OPTIONS
else
docker run \
$(propagate_env_vars \
auto_proxy \
https_proxy \
http_proxy \
no_proxy \
AUTO_PROXY \
HTTPS_PROXY \
HTTP_PROXY \
NO_PROXY \
) \
--shm-size=1g --rm -v "$(pwd)":/sitespeed.io $SITESPEED_IMAGE:$SITESPEED_VERSION --plugins.add ./gitlab-exporter --cpu --outputFolder sitespeed-results "$CI_ENVIRONMENT_URL" $SITESPEED_OPTIONS
fi
- mv sitespeed-results/data/performance.json browser-performance.json
artifacts:
paths:
- sitespeed-results/
reports:
browser_performance: browser-performance.json
rules:
- if: '$CI_KUBERNETES_ACTIVE == null || $CI_KUBERNETES_ACTIVE == ""'
when: never
- if: '$PERFORMANCE_DISABLED'
when: never
- if: '$CI_COMMIT_TAG || $CI_COMMIT_BRANCH'

View File

@ -3,8 +3,8 @@
# Class is used while we're migrating from master to main
module Gitlab
module DefaultBranch
def self.value(project: nil)
Feature.enabled?(:main_branch_over_master, project, default_enabled: :yaml) ? 'main' : 'master'
def self.value(object: nil)
Feature.enabled?(:main_branch_over_master, object, default_enabled: :yaml) ? 'main' : 'master'
end
end
end

View File

@ -6334,7 +6334,7 @@ msgstr ""
msgid "Choose File..."
msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgid "Choose a branch/tag (e.g. %{branch}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
msgid "Choose a file"
@ -17894,7 +17894,7 @@ msgstr ""
msgid "InviteEmail|As a guest, you can view projects, leave comments, and create issues."
msgstr ""
msgid "InviteEmail|As a maintainer, you have full access to projects. You can push commits to master and deploy to production."
msgid "InviteEmail|As a maintainer, you have full access to projects. You can push commits to the default branch and deploy to production."
msgstr ""
msgid "InviteEmail|As a reporter, you can view projects and reports, and leave comments on issues."

View File

@ -186,7 +186,7 @@
},
"devDependencies": {
"@babel/plugin-transform-modules-commonjs": "^7.10.1",
"@gitlab/eslint-plugin": "8.3.0",
"@gitlab/eslint-plugin": "8.4.0",
"@gitlab/stylelint-config": "2.3.0",
"@testing-library/dom": "^7.16.2",
"@vue/test-utils": "1.1.2",
@ -201,7 +201,7 @@
"docdash": "^1.0.2",
"eslint": "7.26.0",
"eslint-import-resolver-jest": "3.0.0",
"eslint-import-resolver-webpack": "0.13.0",
"eslint-import-resolver-webpack": "0.13.1",
"eslint-plugin-jasmine": "4.1.2",
"eslint-plugin-no-jquery": "2.6.0",
"gettext-extractor": "^3.5.3",

View File

@ -8,8 +8,8 @@ if (process.env.RAILS_ENV !== 'production') {
}
const fs = require('fs');
const glob = require('glob');
const path = require('path');
const glob = require('glob');
const pjs = require('postcss');
const paths = glob.sync('public/assets/page_bundles/_mixins_and_variables_and_functions*.css', {

View File

@ -1,8 +1,8 @@
const { resolve } = require('path');
const { sync } = require('glob');
const { createCoverageMap } = require('istanbul-lib-coverage');
const { createContext } = require('istanbul-lib-report');
const { create } = require('istanbul-reports');
const { resolve } = require('path');
const coverageMap = createCoverageMap();

View File

@ -1,3 +1,6 @@
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import AccessorUtilities from '~/lib/utils/accessor';
import {
stripQuotes,
uniqueTokens,
@ -5,6 +8,8 @@ import {
processFilters,
filterToQueryObject,
urlQueryToFilter,
getRecentlyUsedTokenValues,
setTokenValueToRecentlyUsed,
} from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
import {
@ -14,6 +19,12 @@ import {
tokenValuePlain,
} from './mock_data';
const mockStorageKey = 'recent-tokens';
function setLocalStorageAvailability(isAvailable) {
jest.spyOn(AccessorUtilities, 'isLocalStorageAccessSafe').mockReturnValue(isAvailable);
}
describe('Filtered Search Utils', () => {
describe('stripQuotes', () => {
it.each`
@ -249,3 +260,79 @@ describe('urlQueryToFilter', () => {
expect(res).toEqual(result);
});
});
describe('getRecentlyUsedTokenValues', () => {
useLocalStorageSpy();
beforeEach(() => {
localStorage.removeItem(mockStorageKey);
});
it('returns array containing recently used token values from provided recentTokenValuesStorageKey', () => {
setLocalStorageAvailability(true);
const mockExpectedArray = [{ foo: 'bar' }];
localStorage.setItem(mockStorageKey, JSON.stringify(mockExpectedArray));
expect(getRecentlyUsedTokenValues(mockStorageKey)).toEqual(mockExpectedArray);
});
it('returns empty array when provided recentTokenValuesStorageKey does not have anything in localStorage', () => {
setLocalStorageAvailability(true);
expect(getRecentlyUsedTokenValues(mockStorageKey)).toEqual([]);
});
it('returns empty array when when access to localStorage is not available', () => {
setLocalStorageAvailability(false);
expect(getRecentlyUsedTokenValues(mockStorageKey)).toEqual([]);
});
});
describe('setTokenValueToRecentlyUsed', () => {
const mockTokenValue1 = { foo: 'bar' };
const mockTokenValue2 = { bar: 'baz' };
useLocalStorageSpy();
beforeEach(() => {
localStorage.removeItem(mockStorageKey);
});
it('adds provided tokenValue to localStorage for recentTokenValuesStorageKey', () => {
setLocalStorageAvailability(true);
setTokenValueToRecentlyUsed(mockStorageKey, mockTokenValue1);
expect(JSON.parse(localStorage.getItem(mockStorageKey))).toEqual([mockTokenValue1]);
});
it('adds provided tokenValue to localStorage at the top of existing values (i.e. Stack order)', () => {
setLocalStorageAvailability(true);
setTokenValueToRecentlyUsed(mockStorageKey, mockTokenValue1);
setTokenValueToRecentlyUsed(mockStorageKey, mockTokenValue2);
expect(JSON.parse(localStorage.getItem(mockStorageKey))).toEqual([
mockTokenValue2,
mockTokenValue1,
]);
});
it('ensures that provided tokenValue is not added twice', () => {
setLocalStorageAvailability(true);
setTokenValueToRecentlyUsed(mockStorageKey, mockTokenValue1);
setTokenValueToRecentlyUsed(mockStorageKey, mockTokenValue1);
expect(JSON.parse(localStorage.getItem(mockStorageKey))).toEqual([mockTokenValue1]);
});
it('does not add any value when acess to localStorage is not available', () => {
setLocalStorageAvailability(false);
setTokenValueToRecentlyUsed(mockStorageKey, mockTokenValue1);
expect(JSON.parse(localStorage.getItem(mockStorageKey))).toBeNull();
});
});

View File

@ -0,0 +1,228 @@
import { GlFilteredSearchToken } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import {
mockRegularLabel,
mockLabels,
} from 'jest/vue_shared/components/sidebar/labels_select_vue/mock_data';
import { DEFAULT_LABELS } from '~/vue_shared/components/filtered_search_bar/constants';
import {
getRecentlyUsedTokenValues,
setTokenValueToRecentlyUsed,
} from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import { mockLabelToken } from '../mock_data';
jest.mock('~/vue_shared/components/filtered_search_bar/filtered_search_utils');
const mockStorageKey = 'recent-tokens-label_name';
const defaultStubs = {
Portal: true,
GlFilteredSearchToken: {
template: `
<div>
<slot name="view-token"></slot>
<slot name="view"></slot>
</div>
`,
},
GlFilteredSearchSuggestionList: {
template: '<div></div>',
methods: {
getValue: () => '=',
},
},
};
const defaultSlots = {
'view-token': `
<div class="js-view-token">${mockRegularLabel.title}</div>
`,
view: `
<div class="js-view">${mockRegularLabel.title}</div>
`,
};
const mockProps = {
tokenConfig: mockLabelToken,
tokenValue: { data: '' },
tokenActive: false,
tokensListLoading: false,
tokenValues: [],
fnActiveTokenValue: jest.fn(),
defaultTokenValues: DEFAULT_LABELS,
recentTokenValuesStorageKey: mockStorageKey,
fnCurrentTokenValue: jest.fn(),
};
function createComponent({
props = { ...mockProps },
stubs = defaultStubs,
slots = defaultSlots,
} = {}) {
return mount(BaseToken, {
propsData: {
...props,
},
provide: {
portalName: 'fake target',
alignSuggestions: jest.fn(),
suggestionsListClass: 'custom-class',
},
stubs,
slots,
});
}
describe('BaseToken', () => {
let wrapper;
beforeEach(() => {
wrapper = createComponent({
props: {
...mockProps,
tokenValue: { data: `"${mockRegularLabel.title}"` },
tokenValues: mockLabels,
},
});
});
afterEach(() => {
wrapper.destroy();
});
describe('data', () => {
it('calls `getRecentlyUsedTokenValues` to populate `recentTokenValues` when `recentTokenValuesStorageKey` is defined', () => {
expect(getRecentlyUsedTokenValues).toHaveBeenCalledWith(mockStorageKey);
});
});
describe('computed', () => {
describe('currentTokenValue', () => {
it('calls `fnCurrentTokenValue` when it is provided', () => {
// We're disabling lint to trigger computed prop execution for this test.
// eslint-disable-next-line no-unused-vars
const { currentTokenValue } = wrapper.vm;
expect(wrapper.vm.fnCurrentTokenValue).toHaveBeenCalledWith(`"${mockRegularLabel.title}"`);
});
});
describe('activeTokenValue', () => {
it('calls `fnActiveTokenValue` when it is provided', async () => {
wrapper.setProps({
fnCurrentTokenValue: undefined,
});
await wrapper.vm.$nextTick();
// We're disabling lint to trigger computed prop execution for this test.
// eslint-disable-next-line no-unused-vars
const { activeTokenValue } = wrapper.vm;
expect(wrapper.vm.fnActiveTokenValue).toHaveBeenCalledWith(
mockLabels,
`"${mockRegularLabel.title.toLowerCase()}"`,
);
});
});
});
describe('watch', () => {
describe('tokenActive', () => {
let wrapperWithTokenActive;
beforeEach(() => {
wrapperWithTokenActive = createComponent({
props: {
...mockProps,
tokenActive: true,
tokenValue: { data: `"${mockRegularLabel.title}"` },
},
});
});
afterEach(() => {
wrapperWithTokenActive.destroy();
});
it('emits `fetch-token-values` event on the component when value of this prop is changed to false and `tokenValues` array is empty', async () => {
wrapperWithTokenActive.setProps({
tokenActive: false,
});
await wrapperWithTokenActive.vm.$nextTick();
expect(wrapperWithTokenActive.emitted('fetch-token-values')).toBeTruthy();
expect(wrapperWithTokenActive.emitted('fetch-token-values')).toEqual([
[`"${mockRegularLabel.title}"`],
]);
});
});
});
describe('methods', () => {
describe('handleTokenValueSelected', () => {
it('calls `setTokenValueToRecentlyUsed` when `recentTokenValuesStorageKey` is defined', () => {
const mockTokenValue = {
id: 1,
title: 'Foo',
};
wrapper.vm.handleTokenValueSelected(mockTokenValue);
expect(setTokenValueToRecentlyUsed).toHaveBeenCalledWith(mockStorageKey, mockTokenValue);
});
});
});
describe('template', () => {
it('renders gl-filtered-search-token component', () => {
const wrapperWithNoStubs = createComponent({
stubs: {},
});
const glFilteredSearchToken = wrapperWithNoStubs.find(GlFilteredSearchToken);
expect(glFilteredSearchToken.exists()).toBe(true);
expect(glFilteredSearchToken.props('config')).toBe(mockLabelToken);
wrapperWithNoStubs.destroy();
});
it('renders `view-token` slot when present', () => {
expect(wrapper.find('.js-view-token').exists()).toBe(true);
});
it('renders `view` slot when present', () => {
expect(wrapper.find('.js-view').exists()).toBe(true);
});
describe('events', () => {
let wrapperWithNoStubs;
beforeEach(() => {
wrapperWithNoStubs = createComponent({
stubs: { Portal: true },
});
});
afterEach(() => {
wrapperWithNoStubs.destroy();
});
it('emits `fetch-token-values` event on component after a delay when component emits `input` event', async () => {
jest.useFakeTimers();
wrapperWithNoStubs.find(GlFilteredSearchToken).vm.$emit('input', { data: 'foo' });
await wrapperWithNoStubs.vm.$nextTick();
jest.runAllTimers();
expect(wrapperWithNoStubs.emitted('fetch-token-values')).toBeTruthy();
expect(wrapperWithNoStubs.emitted('fetch-token-values')[1]).toEqual(['foo']);
});
});
});
});

View File

@ -289,4 +289,38 @@ RSpec.describe CommitsHelper do
}
end
end
describe "#commit_partial_cache_key" do
subject { helper.commit_partial_cache_key(commit, ref: ref, merge_request: merge_request, request: request) }
let(:commit) { create(:commit).present(current_user: user) }
let(:commit_status) { create(:commit_status) }
let(:user) { create(:user) }
let(:ref) { "master" }
let(:merge_request) { nil }
let(:request) { double(xhr?: true) }
let(:current_path) { "test" }
before do
expect(commit).to receive(:status_for).with(ref).and_return(commit_status)
assign(:path, current_path)
end
it { is_expected.to be_an(Array) }
it { is_expected.to include(commit) }
it { is_expected.to include(commit.author) }
it { is_expected.to include(ref) }
it do
is_expected.to include(
{
merge_request: merge_request,
pipeline_status: Digest::SHA1.hexdigest(commit_status.to_s),
xhr: true,
controller: "commits",
path: current_path
}
)
end
end
end

View File

@ -867,10 +867,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/at.js/-/at.js-1.5.7.tgz#1ee6f838cc4410a1d797770934df91d90df8179e"
integrity sha512-c6ySRK/Ma7lxwpIVbSAF3P+xiTLrNTGTLRx4/pHK111AdFxwgUwrYF6aVZFXvmG65jHOJHoa0eQQ21RW6rm0Rg==
"@gitlab/eslint-plugin@8.3.0":
version "8.3.0"
resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin/-/eslint-plugin-8.3.0.tgz#156a375c6ab9e578ba39080932bca27006413486"
integrity sha512-AuJ6ddKVbfjVUd9DLaNLhpflThZKULWatpUuI+0RhcqyRTmcb1KL5YPxxKDlE1K+faeefgiWaGB+vSNmyNNPQQ==
"@gitlab/eslint-plugin@8.4.0":
version "8.4.0"
resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin/-/eslint-plugin-8.4.0.tgz#094fa4d41676a71146f82e1b19257a7ceabefd88"
integrity sha512-VE/c1yIMrj2igJWAALQtAKpnXL8fN5wJ1uKteZfi8xYbWouoUK6hizXSPrrEUWiM2FqcBI4Igcpz2JlJzDlAnA==
dependencies:
babel-eslint "^10.0.3"
eslint-config-airbnb-base "^14.2.1"
@ -881,6 +881,7 @@
eslint-plugin-jest "^23.8.2"
eslint-plugin-promise "^4.2.1"
eslint-plugin-vue "^7.5.0"
lodash "4.17.20"
vue-eslint-parser "^7.0.0"
"@gitlab/favicon-overlay@2.0.0":
@ -4142,10 +4143,10 @@ debug@3.1.0, debug@~3.1.0:
dependencies:
ms "2.0.0"
debug@^3.1.0, debug@^3.1.1, debug@^3.2.6:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
debug@^3.1.0, debug@^3.1.1, debug@^3.2.6, debug@^3.2.7:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
dependencies:
ms "^2.1.1"
@ -4817,20 +4818,21 @@ eslint-import-resolver-node@^0.3.4:
debug "^2.6.9"
resolve "^1.13.1"
eslint-import-resolver-webpack@0.13.0:
version "0.13.0"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.0.tgz#5cb19cf4b6996c8a2514aeb10f909e2c70488dc3"
integrity sha512-hZWGcmjaJZK/WSCYGI/y4+FMGQZT+cwW/1E/P4rDwFj2PbanlQHISViw4ccDJ+2wxAqjgwBfxwy3seABbVKDEw==
eslint-import-resolver-webpack@0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.1.tgz#6d2fb928091daf2da46efa1e568055555b2de902"
integrity sha512-O/8mG6AHmaKYSMb4lWxiXPpaARxOJ4rMQEHJ8vTgjS1MXooJA3KPgBPPAdOPoV17v5ML5120qod5FBLM+DtgEw==
dependencies:
array-find "^1.0.0"
debug "^2.6.9"
debug "^3.2.7"
enhanced-resolve "^0.9.1"
find-root "^1.1.0"
has "^1.0.3"
interpret "^1.2.0"
lodash "^4.17.15"
node-libs-browser "^1.0.0 || ^2.0.0"
resolve "^1.13.1"
interpret "^1.4.0"
is-core-module "^2.4.0"
is-regex "^1.1.3"
lodash "^4.17.21"
resolve "^1.20.0"
semver "^5.7.1"
eslint-module-utils@^2.6.0:
@ -5896,10 +5898,10 @@ has-flag@^4.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
has-symbols@^1.0.0, has-symbols@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
has-value@^0.3.1:
version "0.3.1"
@ -6311,7 +6313,7 @@ internal-ip@^4.3.0:
default-gateway "^4.2.0"
ipaddr.js "^1.9.0"
interpret@^1.2.0, interpret@^1.4.0:
interpret@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
@ -6409,6 +6411,13 @@ is-ci@^2.0.0:
dependencies:
ci-info "^2.0.0"
is-core-module@^2.2.0, is-core-module@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1"
integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==
dependencies:
has "^1.0.3"
is-data-descriptor@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
@ -6581,13 +6590,13 @@ is-potential-custom-element-name@^1.0.0:
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397"
integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c=
is-regex@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251"
integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==
is-regex@^1.1.1, is-regex@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f"
integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==
dependencies:
call-bind "^1.0.2"
has-symbols "^1.0.1"
has-symbols "^1.0.2"
is-regexp@^2.0.0:
version "2.1.0"
@ -7866,6 +7875,11 @@ lodash.values@^4.3.0:
resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347"
integrity sha1-o6bCsOvsxcLLocF+bmIP6BtT00c=
lodash@4.17.20:
version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
@ -8592,7 +8606,7 @@ node-int64@^0.4.0:
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=
"node-libs-browser@^1.0.0 || ^2.0.0", node-libs-browser@^2.2.1:
node-libs-browser@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425"
integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==
@ -10170,11 +10184,12 @@ resolve-url@^0.2.1:
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0:
version "1.17.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.20.0:
version "1.20.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
dependencies:
is-core-module "^2.2.0"
path-parse "^1.0.6"
responselike@^1.0.2: