Skip to content

Commit 7526a1d

Browse files
authored
Merge pull request #442 from wordpress-mobile/buildkite-actions
Add Builkite-related actions to manipulate annotations and metadata
2 parents 1f9b972 + 7a8ebb5 commit 7526a1d

File tree

5 files changed

+282
-2
lines changed

5 files changed

+282
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
### New Features
1212

13-
_None_
13+
- Add new `buildkite_annotate` action to add/remove annotations from the current build. [#442]
14+
- Add new `buildkite_metadata` action to set/get metadata from the current build. [#442]
1415

1516
### Bug Fixes
1617

@@ -24,7 +25,7 @@ _None_
2425

2526
### New Features
2627

27-
- Added Mac support to all `common` actions and any relevant `ios` actions [#439]
28+
- Add Mac support to all `common` actions and any relevant `ios` actions [#439]
2829

2930
## 6.2.0
3031

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
module Fastlane
2+
module Actions
3+
class BuildkiteAnnotateAction < Action
4+
def self.run(params)
5+
message = params[:message]
6+
context = params[:context]
7+
style = params[:style]
8+
9+
if message.nil?
10+
# Delete an annotation, but swallow the error if the annotation didn't exist — to avoid having
11+
# this action failing or printing a red log for no good reason — hence the `|| true`
12+
ctx_param = "--context #{context.shellescape}" unless context.nil?
13+
sh("buildkite-agent annotation remove #{ctx_param} || true")
14+
else
15+
# Add new annotation using `buildkite-agent`
16+
extra_params = {
17+
context: context,
18+
style: style
19+
}.compact.flat_map { |k, v| ["--#{k}", v] }
20+
sh('buildkite-agent', 'annotate', *extra_params, params[:message])
21+
end
22+
end
23+
24+
#####################################################
25+
# @!group Documentation
26+
#####################################################
27+
28+
def self.description
29+
'Add or remove annotations to the current Buildkite build'
30+
end
31+
32+
def self.details
33+
<<~DETAILS
34+
Add or remove annotations to the current Buildkite build.
35+
36+
Has to be run on a CI job (where a `buildkite-agent` is running), e.g. typically by a lane
37+
that is triggered as part of a Buildkite CI step.
38+
39+
See https://buildkite.com/docs/agent/v3/cli-annotate
40+
DETAILS
41+
end
42+
43+
def self.available_options
44+
[
45+
FastlaneCore::ConfigItem.new(
46+
key: :context,
47+
env_name: 'BUILDKITE_ANNOTATION_CONTEXT',
48+
description: 'The context of the annotation used to differentiate this annotation from others',
49+
type: String,
50+
optional: true
51+
),
52+
FastlaneCore::ConfigItem.new(
53+
key: :style,
54+
env_name: 'BUILDKITE_ANNOTATION_STYLE',
55+
description: 'The style of the annotation (`success`, `info`, `warning` or `error`)',
56+
type: String,
57+
optional: true,
58+
verify_block: proc do |value|
59+
valid_values = %w[success info warning error]
60+
next if value.nil? || valid_values.include?(value)
61+
62+
UI.user_error!("Invalid value `#{value}` for parameter `style`. Valid values are: #{valid_values.join(', ')}")
63+
end
64+
),
65+
FastlaneCore::ConfigItem.new(
66+
key: :message,
67+
description: 'The message to use in the new annotation. Supports GFM-Flavored Markdown. ' \
68+
+ 'If message is nil, any existing annotation with the provided context will be deleted',
69+
type: String,
70+
optional: true,
71+
default_value: nil # nil message = delete existing annotation if any
72+
),
73+
]
74+
end
75+
76+
def self.authors
77+
['Automattic']
78+
end
79+
80+
def self.is_supported?(platform)
81+
true
82+
end
83+
end
84+
end
85+
end
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
module Fastlane
2+
module Actions
3+
class BuildkiteMetadataAction < Action
4+
def self.run(params)
5+
# Set/Add new metadata values
6+
params[:set]&.each do |key, value|
7+
sh('buildkite-agent', 'meta-data', 'set', key.to_s, value.to_s)
8+
end
9+
10+
# Return value of existing metadata key
11+
sh('buildkite-agent', 'meta-data', 'get', params[:get].to_s) unless params[:get].nil?
12+
end
13+
14+
#####################################################
15+
# @!group Documentation
16+
#####################################################
17+
18+
def self.description
19+
'Set/Get metadata to the current Buildkite build'
20+
end
21+
22+
def self.details
23+
<<~DETAILS
24+
Set and/or get metadata to the current Buildkite build.
25+
26+
Has to be run on a CI job (where a `buildkite-agent` is running), e.g. typically by a lane
27+
that is triggered as part of a Buildkite CI step.
28+
29+
See https://buildkite.com/docs/agent/v3/cli-meta-data
30+
DETAILS
31+
end
32+
33+
def self.available_options
34+
[
35+
FastlaneCore::ConfigItem.new(
36+
key: :set,
37+
env_name: 'BUILDKITE_METADATA_SET',
38+
description: 'The hash of key/value pairs of the meta-data to set',
39+
type: Hash,
40+
optional: true,
41+
default_value: nil
42+
),
43+
FastlaneCore::ConfigItem.new(
44+
key: :get,
45+
env_name: 'BUILDKITE_METADATA_GET',
46+
description: 'The key of the metadata to get the value of',
47+
type: String,
48+
optional: true,
49+
default_value: nil
50+
),
51+
]
52+
end
53+
54+
def self.return_value
55+
'The value of the Buildkite metadata corresponding to the provided `get` key. `nil` if no `get` parameter was provided.'
56+
end
57+
58+
def self.authors
59+
['Automattic']
60+
end
61+
62+
def self.is_supported?(platform)
63+
true
64+
end
65+
end
66+
end
67+
end
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
require 'spec_helper'
2+
3+
describe Fastlane::Actions::BuildkiteAnnotateAction do
4+
describe '`style` parameter validation' do
5+
it 'errors if we use an invalid style' do
6+
expect(FastlaneCore::UI).to receive(:user_error!).with('Invalid value `failure` for parameter `style`. Valid values are: success, info, warning, error')
7+
8+
run_described_fastlane_action(
9+
context: 'ctx',
10+
style: 'failure',
11+
message: 'Fake message'
12+
)
13+
end
14+
15+
%w[success info warning error].each do |style|
16+
it "accepts `#{style}` as a valid style" do
17+
expect(FastlaneCore::UI).not_to receive(:user_error!)
18+
cmd = run_described_fastlane_action(
19+
context: 'ctx',
20+
style: style,
21+
message: 'message'
22+
)
23+
expect(cmd).to eq("buildkite-agent annotate --context ctx --style #{style} message")
24+
end
25+
end
26+
27+
it 'accepts `nil` as a valid style' do
28+
expect(FastlaneCore::UI).not_to receive(:user_error!)
29+
cmd = run_described_fastlane_action(
30+
context: 'ctx',
31+
message: 'message'
32+
)
33+
expect(cmd).to eq('buildkite-agent annotate --context ctx message')
34+
end
35+
end
36+
37+
describe 'annotation creation' do
38+
it 'generates the right command to create an annotation when message is provided' do
39+
cmd = run_described_fastlane_action(
40+
context: 'ctx',
41+
style: 'warning',
42+
message: 'message'
43+
)
44+
expect(cmd).to eq('buildkite-agent annotate --context ctx --style warning message')
45+
end
46+
47+
it 'properly escapes the message and context' do
48+
cmd = run_described_fastlane_action(
49+
context: 'some ctx',
50+
style: 'warning',
51+
message: 'a <b>nice</b> message; with fun characters & all…'
52+
)
53+
expect(cmd).to eq('buildkite-agent annotate --context some\ ctx --style warning a\ \<b\>nice\</b\>\ message\;\ with\ fun\ characters\ \&\ all\…')
54+
end
55+
56+
it 'falls back to Buildkite\'s default `context` when none is provided' do
57+
cmd = run_described_fastlane_action(
58+
style: 'warning',
59+
message: 'a nice message'
60+
)
61+
expect(cmd).to eq('buildkite-agent annotate --style warning a\ nice\ message')
62+
end
63+
64+
it 'falls back to Buildkite\'s default `style` when none is provided' do
65+
cmd = run_described_fastlane_action(
66+
context: 'my-ctx',
67+
message: 'a nice message'
68+
)
69+
expect(cmd).to eq('buildkite-agent annotate --context my-ctx a\ nice\ message')
70+
end
71+
end
72+
73+
describe 'annotation deletion' do
74+
it 'generates the right command to delete an annotation when no message is provided' do
75+
cmd = run_described_fastlane_action(
76+
context: 'some ctx',
77+
message: nil
78+
)
79+
expect(cmd).to eq('buildkite-agent annotation remove --context some\ ctx || true')
80+
end
81+
end
82+
end
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
require 'spec_helper'
2+
3+
describe Fastlane::Actions::BuildkiteMetadataAction do
4+
it 'calls the right command to set a single metadata' do
5+
expect(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'set', 'foo', 'bar')
6+
7+
res = run_described_fastlane_action(set: { foo: 'bar' })
8+
expect(res).to be_nil
9+
end
10+
11+
it 'calls the commands as many times as necessary when we want to set multiple metadata at once' do
12+
expect(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'set', 'key1', 'value1')
13+
expect(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'set', 'key2', 'value2')
14+
15+
metadata = {
16+
key1: 'value1',
17+
key2: 'value2'
18+
}
19+
run_described_fastlane_action(set: metadata)
20+
end
21+
22+
it 'calls the right command to get the value of metadata, and returns the right value' do
23+
expect(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'get', 'foo')
24+
allow(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'get', 'foo').and_return('foo value')
25+
26+
res = run_described_fastlane_action(get: 'foo')
27+
expect(res).to eq('foo value')
28+
end
29+
30+
it 'allows both setting and getting metadata in the same call' do
31+
# Might not be the main way we intend to use this action… but it's still supported.
32+
expect(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'set', 'key1', 'value1')
33+
expect(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'set', 'key2', 'value2')
34+
expect(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'get', 'key3')
35+
allow(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'get', 'key3').and_return('value3')
36+
37+
new_metadata = {
38+
key1: 'value1',
39+
key2: 'value2'
40+
}
41+
res = run_described_fastlane_action(set: new_metadata, get: 'key3')
42+
43+
expect(res).to eq('value3')
44+
end
45+
end

0 commit comments

Comments
 (0)