提交 0ff031c7 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 7b8ec6e7
......@@ -19,69 +19,18 @@ export default {
},
computed: {
environment() {
let environmentText;
switch (this.deploymentStatus.status) {
case 'last':
environmentText = sprintf(
__('This job is the most recent deployment to %{link}.'),
{ link: this.environmentLink },
false,
);
break;
return this.lastEnvironmentMessage();
case 'out_of_date':
if (this.hasLastDeployment) {
environmentText = sprintf(
__(
'This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}.',
),
{
environmentLink: this.environmentLink,
deploymentLink: this.deploymentLink(`#${this.lastDeployment.iid}`),
},
false,
);
} else {
environmentText = sprintf(
__('This job is an out-of-date deployment to %{environmentLink}.'),
{ environmentLink: this.environmentLink },
false,
);
}
break;
return this.outOfDateEnvironmentMessage();
case 'failed':
environmentText = sprintf(
__('The deployment of this job to %{environmentLink} did not succeed.'),
{ environmentLink: this.environmentLink },
false,
);
break;
return this.failedEnvironmentMessage();
case 'creating':
if (this.hasLastDeployment) {
environmentText = sprintf(
__(
'This job is creating a deployment to %{environmentLink} and will overwrite the %{deploymentLink}.',
),
{
environmentLink: this.environmentLink,
deploymentLink: this.deploymentLink(__('latest deployment')),
},
false,
);
} else {
environmentText = sprintf(
__('This job is creating a deployment to %{environmentLink}.'),
{ environmentLink: this.environmentLink },
false,
);
}
break;
return this.creatingEnvironmentMessage();
default:
break;
return '';
}
return environmentText && this.hasCluster
? `${environmentText} ${this.clusterText}`
: environmentText;
},
environmentLink() {
if (this.hasEnvironment) {
......@@ -137,11 +86,6 @@ export default {
false,
);
},
clusterText() {
return this.hasCluster
? sprintf(__('Cluster %{cluster} was used.'), { cluster: this.clusterNameOrLink }, false)
: '';
},
},
methods: {
deploymentLink(name) {
......@@ -155,6 +99,91 @@ export default {
false,
);
},
failedEnvironmentMessage() {
const { environmentLink } = this;
return sprintf(
__('The deployment of this job to %{environmentLink} did not succeed.'),
{ environmentLink },
false,
);
},
lastEnvironmentMessage() {
const { environmentLink, clusterNameOrLink, hasCluster } = this;
const message = hasCluster
? __('This job is deployed to %{environmentLink} using cluster %{clusterNameOrLink}.')
: __('This job is deployed to %{environmentLink}.');
return sprintf(message, { environmentLink, clusterNameOrLink }, false);
},
outOfDateEnvironmentMessage() {
const { hasLastDeployment, hasCluster, environmentLink, clusterNameOrLink } = this;
if (hasLastDeployment) {
const message = hasCluster
? __(
'This job is an out-of-date deployment to %{environmentLink} using cluster %{clusterNameOrLink}. View the %{deploymentLink}.',
)
: __(
'This job is an out-of-date deployment to %{environmentLink}. View the %{deploymentLink}.',
);
return sprintf(
message,
{
environmentLink,
clusterNameOrLink,
deploymentLink: this.deploymentLink(__('most recent deployment')),
},
false,
);
}
const message = hasCluster
? __(
'This job is an out-of-date deployment to %{environmentLink} using cluster %{clusterNameOrLink}.',
)
: __('This job is an out-of-date deployment to %{environmentLink}.');
return sprintf(
message,
{
environmentLink,
clusterNameOrLink,
},
false,
);
},
creatingEnvironmentMessage() {
const { hasLastDeployment, hasCluster, environmentLink, clusterNameOrLink } = this;
if (hasLastDeployment) {
const message = hasCluster
? __(
'This job is creating a deployment to %{environmentLink} using cluster %{clusterNameOrLink}. This will overwrite the %{deploymentLink}.',
)
: __(
'This job is creating a deployment to %{environmentLink}. This will overwrite the %{deploymentLink}.',
);
return sprintf(
message,
{
environmentLink,
clusterNameOrLink,
deploymentLink: this.deploymentLink(__('latest deployment')),
},
false,
);
}
return sprintf(
__('This job is creating a deployment to %{environmentLink}.'),
{ environmentLink },
false,
);
},
},
};
</script>
......
......@@ -57,8 +57,7 @@ module AtomicInternalId
end
define_method("track_#{scope}_#{column}!") do
iid_always_track = Feature.enabled?(:iid_always_track, default_enabled: true)
return unless @internal_id_needs_tracking || iid_always_track
return unless @internal_id_needs_tracking
scope_value = internal_id_read_scope(scope)
return unless scope_value
......
......@@ -54,7 +54,7 @@ class InternalId < ApplicationRecord
last_value
end
# Temporary instrumentation to track for-update locks
# Instrumentation to track for-update locks
def update_and_save_counter
strong_memoize(:update_and_save_counter) do
Gitlab::Metrics.counter(:gitlab_internal_id_for_update_lock, 'Number of ROW SHARE (FOR UPDATE) locks on individual records from internal_ids')
......
......@@ -16,9 +16,7 @@
%thead
%tr
%th
= s_("ProtectedBranch|Protected branch (%{protected_branches_count})") % { protected_branches_count: @protected_branches_count }
%th
= s_("ProtectedBranch|Last commit")
= s_("ProtectedBranch|Branch")
%th
= s_("ProtectedBranch|Allowed to merge")
%th
......
......@@ -5,17 +5,14 @@
%span.ref-name= protected_branch.name
- if @project.root_ref?(protected_branch.name)
%span.badge.badge-info.prepend-left-5 default
%td
- if protected_branch.wildcard?
- matching_branches = protected_branch.matching(repository.branches)
= link_to pluralize(matching_branches.count, "matching branch"), namespace_project_protected_branch_path(@project.namespace, @project, protected_branch)
- else
- if commit = protected_branch.commit
= link_to(commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit-sha')
= time_ago_with_tooltip(commit.committed_date)
- else
(branch was deleted from repository)
%span.badge.badge-info.d-inline default
%div
- if protected_branch.wildcard?
- matching_branches = protected_branch.matching(repository.branches)
= link_to pluralize(matching_branches.count, "matching branch"), namespace_project_protected_branch_path(@project.namespace, @project, protected_branch)
- elsif !protected_branch.commit
%span.text-muted Branch was deleted.
= yield
......
---
title: Update cluster link text
merge_request: 18322
author:
type: changed
---
title: Add matching branch info to branch column
merge_request: 18352
author:
type: added
---
title: Avoid unnecessary locks on internal_ids
merge_request: 18328
author:
type: performance
......@@ -47,6 +47,8 @@ to be enabled:
when an issue is deleted.
- Design Management
[isn't supported by Geo](https://gitlab.com/groups/gitlab-org/-/epics/1633) yet.
- Only the latest version of the designs can be deleted.
- Deleted designs cannot be recovered but you can see them on previous designs versions.
## The Design Management page
......@@ -77,6 +79,34 @@ to help summarize changes between versions.
| Modified (in the selected version) | ![Design Modified](img/design_modified_v12_3.png) |
| Added (in the selected version) | ![Design Added](img/design_added_v12_3.png) |
## Deleting designs
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/11089) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.4.
There are two ways to delete designs: manually delete them
individually, or select a few of them to delete at once,
as shown below.
To delete a single design, click it to view it enlarged,
then click the trash icon on the top right corner and confirm
the deletion by clicking the **Delete** button on the modal window:
![Confirm design deletion](img/confirm_design_deletion_v12_4.png)
To delete multiple designs at once, on the design's list view,
first select the designs you want to delete:
![Select designs](img/select_designs_v12_4.png)
Once selected, click the **Delete selected** button to confirm the deletion:
![Delete multiple designs](img/delete_multiple_designs_v12_4.png)
NOTE: **Note:**
Only the latest version of the designs can be deleted.
Deleted designs are not permanently lost; they can be
viewed by browsing previous versions.
## Adding annotations to designs
When a design image is displayed, you can add annotations to it by clicking on
......
......@@ -3348,9 +3348,6 @@ msgstr ""
msgid "Closes this %{quick_action_target}."
msgstr ""
msgid "Cluster %{cluster} was used."
msgstr ""
msgid "Cluster Health"
msgstr ""
......@@ -5448,6 +5445,9 @@ msgstr ""
msgid "DesignManagement|An error occurred while loading designs. Please try again."
msgstr ""
msgid "DesignManagement|Are you sure you want to delete the selected designs?"
msgstr ""
msgid "DesignManagement|Could not add a new comment. Please try again"
msgstr ""
......@@ -5457,6 +5457,18 @@ msgstr ""
msgid "DesignManagement|Could not find design, please try again."
msgstr ""
msgid "DesignManagement|Delete"
msgstr ""
msgid "DesignManagement|Delete designs confirmation"
msgstr ""
msgid "DesignManagement|Delete selected"
msgstr ""
msgid "DesignManagement|Deselect all"
msgstr ""
msgid "DesignManagement|Error uploading a new design. Please try again"
msgstr ""
......@@ -5475,6 +5487,9 @@ msgstr ""
msgid "DesignManagement|Requested design version does not exist. Showing latest version instead"
msgstr ""
msgid "DesignManagement|Select all"
msgstr ""
msgid "DesignManagement|The maximum number of designs allowed to be uploaded is %{upload_limit}. Please try again."
msgstr ""
......@@ -5484,6 +5499,9 @@ msgstr ""
msgid "DesignManagement|Upload and view the latest designs for this issue. Consistent and easy to find, so everyone is up to date."
msgstr ""
msgid "DesignManagement|We could not delete design(s). Please try again."
msgstr ""
msgid "Designs"
msgstr ""
......@@ -13088,10 +13106,10 @@ msgstr ""
msgid "ProtectedBranch|Allowed to push:"
msgstr ""
msgid "ProtectedBranch|Code owner approval"
msgid "ProtectedBranch|Branch"
msgstr ""
msgid "ProtectedBranch|Last commit"
msgid "ProtectedBranch|Code owner approval"
msgstr ""
msgid "ProtectedBranch|Protect"
......@@ -14369,6 +14387,9 @@ msgstr ""
msgid "Security Reports|Dismissed '%{vulnerabilityName}'"
msgstr ""
msgid "Security Reports|Dismissed '%{vulnerabilityName}'. Turn off the hide dismissed toggle to view."
msgstr ""
msgid "Security Reports|Either you don't have permission to view this dashboard or the dashboard has not been setup. Please check your permission settings with your administrator or check your dashboard configurations to proceed."
msgstr ""
......@@ -16662,21 +16683,36 @@ msgstr ""
msgid "This job has not started yet"
msgstr ""
msgid "This job is an out-of-date deployment to %{environmentLink} using cluster %{clusterNameOrLink}."
msgstr ""
msgid "This job is an out-of-date deployment to %{environmentLink} using cluster %{clusterNameOrLink}. View the %{deploymentLink}."
msgstr ""
msgid "This job is an out-of-date deployment to %{environmentLink}."
msgstr ""
msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
msgid "This job is an out-of-date deployment to %{environmentLink}. View the %{deploymentLink}."
msgstr ""
msgid "This job is archived. Only the complete pipeline can be retried."
msgstr ""
msgid "This job is creating a deployment to %{environmentLink} and will overwrite the %{deploymentLink}."
msgid "This job is creating a deployment to %{environmentLink} using cluster %{clusterNameOrLink}. This will overwrite the %{deploymentLink}."
msgstr ""
msgid "This job is creating a deployment to %{environmentLink}."
msgstr ""
msgid "This job is creating a deployment to %{environmentLink}. This will overwrite the %{deploymentLink}."
msgstr ""
msgid "This job is deployed to %{environmentLink} using cluster %{clusterNameOrLink}."
msgstr ""
msgid "This job is deployed to %{environmentLink}."
msgstr ""
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
......@@ -16692,9 +16728,6 @@ msgstr ""
msgid "This job is stuck because you don't have any active runners that can run this job."
msgstr ""
msgid "This job is the most recent deployment to %{link}."
msgstr ""
msgid "This job requires a manual action"
msgstr ""
......@@ -19713,6 +19746,9 @@ msgstr ""
msgid "missing"
msgstr ""
msgid "most recent deployment"
msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} and %{mergeCommitCount} will be added to %{targetBranch}."
msgstr ""
......
......@@ -534,7 +534,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
it 'shows deployment message' do
expect(page).to have_content 'This job is the most recent deployment to production'
expect(page).to have_content 'This job is deployed to production'
expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}")
end
......@@ -548,14 +548,14 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
it 'shows the name of the cluster' do
expect(page).to have_content 'Cluster the-cluster was used'
expect(page).to have_content 'using cluster the-cluster'
end
context 'when the user is not able to view the cluster' do
let(:user_access_level) { :developer }
it 'includes only the name of the cluster without a link' do
expect(page).to have_content 'Cluster the-cluster was used'
expect(page).to have_content 'using cluster the-cluster'
expect(page).not_to have_link 'the-cluster'
end
end
......@@ -623,8 +623,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
let!(:second_deployment) { create(:deployment, :success, environment: environment, deployable: second_build) }
it 'shows deployment message' do
expected_text = 'This job is an out-of-date deployment ' \
"to staging. View the most recent deployment ##{second_deployment.iid}."
expected_text = 'This job is an out-of-date deployment to staging. View the most recent deployment.'
expect(page).to have_css('.environment-information', text: expected_text)
end
......
......@@ -92,7 +92,10 @@ describe 'Protected Branches', :js do
set_protected_branch_name('some-branch')
click_on "Protect"
within(".protected-branches-list") { expect(page).to have_content(commit.id[0..7]) }
within(".protected-branches-list") do
expect(page).not_to have_content("matching")
expect(page).not_to have_content("was deleted")
end
end
it "displays an error message if the named branch does not exist" do
......@@ -101,7 +104,7 @@ describe 'Protected Branches', :js do
set_protected_branch_name('some-branch')
click_on "Protect"
within(".protected-branches-list") { expect(page).to have_content('branch was deleted') }
within(".protected-branches-list") { expect(page).to have_content('Branch was deleted') }
end
end
......@@ -127,7 +130,6 @@ describe 'Protected Branches', :js do
click_on "Protect"
within(".protected-branches-list") do
expect(page).to have_content("Protected branch (2)")
expect(page).to have_content("2 matching branches")
end
end
......
......@@ -2,6 +2,9 @@ import Vue from 'vue';
import component from '~/jobs/components/environments_block.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
const TEST_CLUSTER_NAME = 'test_cluster';
const TEST_CLUSTER_PATH = 'path/to/test_cluster';
describe('Environments block', () => {
const Component = Vue.extend(component);
let vm;
......@@ -20,22 +23,53 @@ describe('Environments block', () => {
const lastDeployment = { iid: 'deployment', deployable: { build_path: 'bar' } };
const createEnvironmentWithLastDeployment = () => ({
...environment,
last_deployment: { ...lastDeployment },
});
const createEnvironmentWithCluster = () => ({
...environment,
last_deployment: {
...lastDeployment,
cluster: { name: TEST_CLUSTER_NAME, path: TEST_CLUSTER_PATH },
},
});
const createComponent = (deploymentStatus = {}) => {
vm = mountComponent(Component, {
deploymentStatus,
iconStatus: status,
});
};
const findText = () => vm.$el.textContent.trim();
const findJobDeploymentLink = () => vm.$el.querySelector('.js-job-deployment-link');
const findEnvironmentLink = () => vm.$el.querySelector('.js-environment-link');
const findClusterLink = () => vm.$el.querySelector('.js-job-cluster-link');
afterEach(() => {
vm.$destroy();
});
describe('with last deployment', () => {
it('renders info for most recent deployment', () => {
vm = mountComponent(Component, {
deploymentStatus: {
status: 'last',
environment,
},
iconStatus: status,
createComponent({
status: 'last',
environment,
});
expect(vm.$el.textContent.trim()).toEqual(
'This job is the most recent deployment to environment.',
expect(findText()).toEqual('This job is deployed to environment.');
});
it('renders info with cluster', () => {
createComponent({
status: 'last',
environment: createEnvironmentWithCluster(),
});
expect(findText()).toEqual(
`This job is deployed to environment using cluster ${TEST_CLUSTER_NAME}.`,
);
});
});
......@@ -43,133 +77,106 @@ describe('Environments block', () => {
describe('with out of date deployment', () => {
describe('with last deployment', () => {
it('renders info for out date and most recent', () => {
vm = mountComponent(Component, {
deploymentStatus: {
status: 'out_of_date',
environment: Object.assign({}, environment, {
last_deployment: lastDeployment,
}),
},
iconStatus: status,
createComponent({
status: 'out_of_date',
environment: createEnvironmentWithLastDeployment(),
});
expect(vm.$el.textContent.trim()).toEqual(
'This job is an out-of-date deployment to environment. View the most recent deployment #deployment.',
expect(findText()).toEqual(
'This job is an out-of-date deployment to environment. View the most recent deployment.',
);
expect(vm.$el.querySelector('.js-job-deployment-link').getAttribute('href')).toEqual('bar');
expect(findJobDeploymentLink().getAttribute('href')).toEqual('bar');
});
it('renders info with cluster', () => {
createComponent({
status: 'out_of_date',
environment: createEnvironmentWithCluster(),
});
expect(findText()).toEqual(
`This job is an out-of-date deployment to environment using cluster ${TEST_CLUSTER_NAME}. View the most recent deployment.`,
);
});
});
describe('without last deployment', () => {
it('renders info about out of date deployment', () => {
vm = mountComponent(Component, {
deploymentStatus: {
status: 'out_of_date',
environment,
},
iconStatus: status,
createComponent({
status: 'out_of_date',
environment,
});
expect(vm.$el.textContent.trim()).toEqual(
'This job is an out-of-date deployment to environment.',
);
expect(findText()).toEqual('This job is an out-of-date deployment to environment.');
});
});
});
describe('with failed deployment', () => {
it('renders info about failed deployment', () => {
vm = mountComponent(Component, {
deploymentStatus: {
status: 'failed',
environment,
},
iconStatus: status,
createComponent({
status: 'failed',
environment,
});
expect(vm.$el.textContent.trim()).toEqual(
'The deployment of this job to environment did not succeed.',
);
expect(findText()).toEqual('The deployment of this job to environment did not succeed.');
});
});
describe('creating deployment', () => {
describe('with last deployment', () => {
it('renders info about creating deployment and overriding latest deployment', () => {
vm = mountComponent(Component, {
deploymentStatus: {
status: 'creating',
environment: Object.assign({}, environment, {
last_deployment: lastDeployment,
}),
},
iconStatus: status,
createComponent({
status: 'creating',
environment: createEnvironmentWithLastDeployment(),
});
expect(vm.$el.textContent.trim()).toEqual(
'This job is creating a deployment to environment and will overwrite the latest deployment.',
expect(findText()).toEqual(
'This job is creating a deployment to environment. This will overwrite the latest deployment.',
);
expect(vm.$el.querySelector('.js-job-deployment-link').getAttribute('href')).toEqual('bar');
expect(findJobDeploymentLink().getAttribute('href')).toEqual('bar');
expect(findEnvironmentLink().getAttribute('href')).toEqual(environment.environment_path);
expect(findClusterLink()).toBeNull();
});
});
describe('without last deployment', () => {
it('renders info about failed deployment', () => {
vm = mountComponent(Component, {
deploymentStatus: {
status: 'creating',
environment,
},
iconStatus: status,
createComponent({
status: 'creating',
environment,
});
expect(vm.$el.textContent.trim()).toEqual(
'This job is creating a deployment to environment.',
);
expect(findText()).toEqual('This job is creating a deployment to environment.');
});
});
describe('without environment', () => {
it('does not render environment link', () => {
vm = mountComponent(Component, {
deploymentStatus: {
status: 'creating',
environment: null,
},
iconStatus: status,
createComponent({
status: 'creating',
environment: null,
});
expect(vm.$el.querySelector('.js-environment-link')).toBeNull();
expect(findEnvironmentLink()).toBeNull();
});
});
});
describe('with a cluster', () => {
it('renders the cluster link', () => {
const cluster = {
name: 'the-cluster',
path: '/the-cluster-path',
};
vm = mountComponent(Component, {
deploymentStatus: {
status: 'last',
environment: Object.assign({}, environment, {
last_deployment: {
...lastDeployment,
cluster,
},
}),
},
iconStatus: status,
createComponent({
status: 'last',
environment: createEnvironmentWithCluster(),
});
expect(vm.$el.textContent.trim()).toContain('Cluster the-cluster was used.');
expect(vm.$el.querySelector('.js-job-cluster-link').getAttribute('href')).toEqual(
'/the-cluster-path',
expect(findText()).toEqual(
`This job is deployed to environment using cluster ${TEST_CLUSTER_NAME}.`,
);
expect(findClusterLink().getAttribute('href')).toEqual(TEST_CLUSTER_PATH);
});
describe('when the cluster is missing the path', () => {
......@@ -177,39 +184,20 @@ describe('Environments block', () => {
const cluster = {
name: 'the-cluster',
};
vm = mountComponent(Component, {
deploymentStatus: {
status: 'last',
environment: Object.assign({}, environment, {
last_deployment: {
...lastDeployment,
cluster,
},
}),
},
iconStatus: status,
});
expect(vm.$el.textContent.trim()).toContain('Cluster the-cluster was used.');
expect(vm.$el.querySelector('.js-job-cluster-link')).toBeNull();
});
});
});
describe('without a cluster', () => {
it('does not render a cluster link', () => {
vm = mountComponent(Component, {
deploymentStatus: {
createComponent({
status: 'last',
environment: Object.assign({}, environment, {
last_deployment: lastDeployment,
last_deployment: {
...lastDeployment,
cluster,
},
}),
},
iconStatus: status,
});
});
expect(findText()).toContain('using cluster the-cluster.');
expect(vm.$el.querySelector('.js-job-cluster-link')).toBeNull();
expect(findClusterLink()).toBeNull();
});
});
});
});
......@@ -22,41 +22,22 @@ describe AtomicInternalId do
end
context 'when value is set by ensure_project_iid!' do
context 'with iid_always_track false' do
before do
stub_feature_flags(iid_always_track: false)
end
it 'does not track the value' do
expect(InternalId).not_to receive(:track_greatest)
it 'does not track the value' do
expect(InternalId).not_to receive(:track_greatest)
milestone.ensure_project_iid!
subject
end
it 'tracks the iid for the scope that is actually present' do
milestone.iid = external_iid
expect(InternalId).to receive(:track_greatest).once.with(milestone, scope_attrs, usage, external_iid, anything)
expect(InternalId).not_to receive(:generate_next)
# group scope is not present here, the milestone does not have a group
milestone.track_group_iid!
subject
end
milestone.ensure_project_iid!
subject
end
context 'with iid_always_track enabled' do
before do
stub_feature_flags(iid_always_track: true)
end
it 'tracks the iid for the scope that is actually present' do
milestone.iid = external_iid
it 'does not track the value' do
expect(InternalId).to receive(:track_greatest)
expect(InternalId).to receive(:track_greatest).once.with(milestone, scope_attrs, usage, external_iid, anything)
expect(InternalId).not_to receive(:generate_next)
milestone.ensure_project_iid!
subject
end
# group scope is not present here, the milestone does not have a group
milestone.track_group_iid!
subject
end
end
end
......
......@@ -47,10 +47,6 @@ shared_examples_for 'AtomicInternalId' do |validate_presence: true|
end
describe 'internal id generation' do
before do
stub_feature_flags(iid_always_track: false)
end
subject { instance.save! }
it 'calls InternalId.generate_next and sets internal id attribute' do
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册