Commit 0b0d04b4 authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch 'fix/gb/fix-chatops-deploy-multiple-actions-matching' into 'master'

Improve deploy environment chatops slash command

Closes #34748

See merge request !13150
parents fbd4b7ae 07bcabb3
---
title: Improve deploy environment chatops slash command
merge_request: 13150
author:
...@@ -2,7 +2,11 @@ ...@@ -2,7 +2,11 @@
Slash commands in Mattermost and Slack allow you to control GitLab and view GitLab content right inside your chat client, without having to leave it. For Slack, this requires a [project service configuration](../user/project/integrations/slack_slash_commands.md). Simply type the command as a message in your chat client to activate it. Slash commands in Mattermost and Slack allow you to control GitLab and view GitLab content right inside your chat client, without having to leave it. For Slack, this requires a [project service configuration](../user/project/integrations/slack_slash_commands.md). Simply type the command as a message in your chat client to activate it.
Commands are scoped to a project, with a trigger term that is specified during configuration. (We suggest you use the project name as the trigger term for simplicty and clarity.) Taking the trigger term as `project-name`, the commands are: Commands are scoped to a project, with a trigger term that is specified during configuration.
We suggest you use the project name as the trigger term for simplicity and clarity.
Taking the trigger term as `project-name`, the commands are:
| Command | Effect | | Command | Effect |
...@@ -12,3 +16,18 @@ Commands are scoped to a project, with a trigger term that is specified during c ...@@ -12,3 +16,18 @@ Commands are scoped to a project, with a trigger term that is specified during c
| `/project-name issue show <id>` | Shows the issue with id `<id>` | | `/project-name issue show <id>` | Shows the issue with id `<id>` |
| `/project-name issue search <query>` | Shows up to 5 issues matching `<query>` | | `/project-name issue search <query>` | Shows up to 5 issues matching `<query>` |
| `/project-name deploy <from> to <to>` | Deploy from the `<from>` environment to the `<to>` environment | | `/project-name deploy <from> to <to>` | Deploy from the `<from>` environment to the `<to>` environment |
## Issue commands
It is possible to create new issue, display issue details and search up to 5 issues.
## Deploy command
In order to deploy to an environment, GitLab will try to find a deployment
manual action in the pipeline.
If there is only one action for a given environment, it is going to be triggered.
If there is more than one action defined, GitLab will try to find an action
which name equals the environment name we want to deploy to.
Command will return an error when no matching action has been found.
...@@ -21,29 +21,34 @@ module Gitlab ...@@ -21,29 +21,34 @@ module Gitlab
from = match[:from] from = match[:from]
to = match[:to] to = match[:to]
actions = find_actions(from, to) action = find_action(from, to)
if actions.none? if action.nil?
Gitlab::SlashCommands::Presenters::Deploy.new(nil).no_actions Gitlab::SlashCommands::Presenters::Deploy
elsif actions.one? .new(action).action_not_found
action = play!(from, to, actions.first)
Gitlab::SlashCommands::Presenters::Deploy.new(action).present(from, to)
else else
Gitlab::SlashCommands::Presenters::Deploy.new(actions).too_many_actions deployment = action.play(current_user)
Gitlab::SlashCommands::Presenters::Deploy
.new(deployment).present(from, to)
end end
end end
private private
def play!(from, to, action) def find_action(from, to)
action.play(current_user)
end
def find_actions(from, to)
environment = project.environments.find_by(name: from) environment = project.environments.find_by(name: from)
return [] unless environment return unless environment
environment.actions_for(to).select(&:starts_environment?) actions = environment.actions_for(to).select do |action|
action.starts_environment?
end
if actions.many?
actions.find { |action| action.name == to.to_s }
else
actions.first
end
end end
end end
end end
......
...@@ -3,17 +3,14 @@ module Gitlab ...@@ -3,17 +3,14 @@ module Gitlab
module Presenters module Presenters
class Deploy < Presenters::Base class Deploy < Presenters::Base
def present(from, to) def present(from, to)
message = "Deployment started from #{from} to #{to}. [Follow its progress](#{resource_url})." message = "Deployment started from #{from} to #{to}. " \
"[Follow its progress](#{resource_url})."
in_channel_response(text: message) in_channel_response(text: message)
end end
def no_actions def action_not_found
ephemeral_response(text: "No action found to be executed") ephemeral_response(text: "Couldn't find a deployment manual action.")
end
def too_many_actions
ephemeral_response(text: "Too many actions defined")
end end
end end
end end
......
...@@ -80,7 +80,7 @@ describe Gitlab::SlashCommands::Command do ...@@ -80,7 +80,7 @@ describe Gitlab::SlashCommands::Command do
it 'returns error' do it 'returns error' do
expect(subject[:response_type]).to be(:ephemeral) expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to include('Too many actions defined') expect(subject[:text]).to include("Couldn't find a deployment manual action.")
end end
end end
end end
......
...@@ -22,7 +22,7 @@ describe Gitlab::SlashCommands::Deploy do ...@@ -22,7 +22,7 @@ describe Gitlab::SlashCommands::Deploy do
context 'if no environment is defined' do context 'if no environment is defined' do
it 'does not execute an action' do it 'does not execute an action' do
expect(subject[:response_type]).to be(:ephemeral) expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to eq("No action found to be executed") expect(subject[:text]).to eq "Couldn't find a deployment manual action."
end end
end end
...@@ -35,12 +35,12 @@ describe Gitlab::SlashCommands::Deploy do ...@@ -35,12 +35,12 @@ describe Gitlab::SlashCommands::Deploy do
context 'without actions' do context 'without actions' do
it 'does not execute an action' do it 'does not execute an action' do
expect(subject[:response_type]).to be(:ephemeral) expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to eq("No action found to be executed") expect(subject[:text]).to eq "Couldn't find a deployment manual action."
end end
end end
context 'with action' do context 'when single action has been matched' do
let!(:manual1) do before do
create(:ci_build, :manual, pipeline: pipeline, create(:ci_build, :manual, pipeline: pipeline,
name: 'first', name: 'first',
environment: 'production') environment: 'production')
...@@ -48,31 +48,61 @@ describe Gitlab::SlashCommands::Deploy do ...@@ -48,31 +48,61 @@ describe Gitlab::SlashCommands::Deploy do
it 'returns success result' do it 'returns success result' do
expect(subject[:response_type]).to be(:in_channel) expect(subject[:response_type]).to be(:in_channel)
expect(subject[:text]).to start_with('Deployment started from staging to production') expect(subject[:text])
.to start_with('Deployment started from staging to production')
end end
end
context 'when more than one action has been matched' do
context 'when there is no specific actions with a environment name' do
before do
create(:ci_build, :manual, pipeline: pipeline,
name: 'first',
environment: 'production')
context 'when duplicate action exists' do
let!(:manual2) do
create(:ci_build, :manual, pipeline: pipeline, create(:ci_build, :manual, pipeline: pipeline,
name: 'second', name: 'second',
environment: 'production') environment: 'production')
end end
it 'returns error' do it 'returns error about too many actions defined' do
expect(subject[:text]).to eq("Couldn't find a deployment manual action.")
expect(subject[:response_type]).to be(:ephemeral) expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to eq('Too many actions defined')
end end
end end
context 'when teardown action exists' do context 'when one of the actions is environement specific action' do
let!(:teardown) do before do
create(:ci_build, :manual, pipeline: pipeline,
name: 'first',
environment: 'production')
create(:ci_build, :manual, pipeline: pipeline,
name: 'production',
environment: 'production')
end
it 'deploys to production' do
expect(subject[:text])
.to start_with('Deployment started from staging to production')
expect(subject[:response_type]).to be(:in_channel)
end
end
context 'when one of the actions is a teardown action' do
before do
create(:ci_build, :manual, pipeline: pipeline,
name: 'first',
environment: 'production')
create(:ci_build, :manual, :teardown_environment, create(:ci_build, :manual, :teardown_environment,
pipeline: pipeline, name: 'teardown', environment: 'production') pipeline: pipeline, name: 'teardown', environment: 'production')
end end
it 'returns the success message' do it 'deploys to production' do
expect(subject[:text])
.to start_with('Deployment started from staging to production')
expect(subject[:response_type]).to be(:in_channel) expect(subject[:response_type]).to be(:in_channel)
expect(subject[:text]).to start_with('Deployment started from staging to production')
end end
end end
end end
......
...@@ -17,8 +17,8 @@ describe Gitlab::SlashCommands::Presenters::Deploy do ...@@ -17,8 +17,8 @@ describe Gitlab::SlashCommands::Presenters::Deploy do
end end
end end
describe '#no_actions' do describe '#action_not_found' do
subject { described_class.new(nil).no_actions } subject { described_class.new(nil).action_not_found }
it { is_expected.to have_key(:text) } it { is_expected.to have_key(:text) }
it { is_expected.to have_key(:response_type) } it { is_expected.to have_key(:response_type) }
...@@ -27,21 +27,7 @@ describe Gitlab::SlashCommands::Presenters::Deploy do ...@@ -27,21 +27,7 @@ describe Gitlab::SlashCommands::Presenters::Deploy do
it 'tells the user there is no action' do it 'tells the user there is no action' do
expect(subject[:response_type]).to be(:ephemeral) expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to eq("No action found to be executed") expect(subject[:text]).to eq "Couldn't find a deployment manual action."
end
end
describe '#too_many_actions' do
subject { described_class.new([]).too_many_actions }
it { is_expected.to have_key(:text) }
it { is_expected.to have_key(:response_type) }
it { is_expected.to have_key(:status) }
it { is_expected.not_to have_key(:attachments) }
it 'tells the user there is no action' do
expect(subject[:response_type]).to be(:ephemeral)
expect(subject[:text]).to eq("Too many actions defined")
end end
end 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