Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ _None_

### New Features

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

### Bug Fixes

Expand All @@ -24,7 +25,7 @@ _None_

### New Features

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

## 6.2.0

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
module Fastlane
module Actions
class BuildkiteAnnotateAction < Action
def self.run(params)
message = params[:message]
context = params[:context]
style = params[:style]

if message.nil?
# Delete an annotation, but swallow the error if the annotation didn't exist — to avoid having
# this action failing or printing a red log for no good reason — hence the `|| true`
ctx_param = "--context #{context.shellescape}" unless context.nil?
sh("buildkite-agent annotation remove #{ctx_param} || true")
else
# Add new annotation using `buildkite-agent`
extra_params = {
context: context,
style: style
}.compact.flat_map { |k, v| ["--#{k}", v] }
sh('buildkite-agent', 'annotate', *extra_params, params[:message])
end
end

#####################################################
# @!group Documentation
#####################################################

def self.description
'Add or remove annotations to the current Buildkite build'
end

def self.details
<<~DETAILS
Add or remove annotations to the current Buildkite build.

Has to be run on a CI job (where a `buildkite-agent` is running), e.g. typically by a lane
that is triggered as part of a Buildkite CI step.

See https://buildkite.com/docs/agent/v3/cli-annotate
DETAILS
end

def self.available_options
[
FastlaneCore::ConfigItem.new(
key: :context,
env_name: 'BUILDKITE_ANNOTATION_CONTEXT',
description: 'The context of the annotation used to differentiate this annotation from others',
type: String,
optional: true
),
FastlaneCore::ConfigItem.new(
key: :style,
env_name: 'BUILDKITE_ANNOTATION_STYLE',
description: 'The style of the annotation (`success`, `info`, `warning` or `error`)',
type: String,
optional: true,
verify_block: proc do |value|
valid_values = %w[success info warning error]
next if value.nil? || valid_values.include?(value)

UI.user_error!("Invalid value `#{value}` for parameter `style`. Valid values are: #{valid_values.join(', ')}")
end
),
FastlaneCore::ConfigItem.new(
key: :message,
description: 'The message to use in the new annotation. Supports GFM-Flavored Markdown. ' \
+ 'If message is nil, any existing annotation with the provided context will be deleted',
type: String,
optional: true,
default_value: nil # nil message = delete existing annotation if any
),
]
end

def self.authors
['Automattic']
end

def self.is_supported?(platform)
true
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
module Fastlane
module Actions
class BuildkiteMetadataAction < Action
def self.run(params)
# Set/Add new metadata values
params[:set]&.each do |key, value|
sh('buildkite-agent', 'meta-data', 'set', key.to_s, value.to_s)
end

# Return value of existing metadata key
sh('buildkite-agent', 'meta-data', 'get', params[:get].to_s) unless params[:get].nil?
end

#####################################################
# @!group Documentation
#####################################################

def self.description
'Set/Get metadata to the current Buildkite build'
end

def self.details
<<~DETAILS
Set and/or get metadata to the current Buildkite build.

Has to be run on a CI job (where a `buildkite-agent` is running), e.g. typically by a lane
that is triggered as part of a Buildkite CI step.

See https://buildkite.com/docs/agent/v3/cli-meta-data
DETAILS
end

def self.available_options
[
FastlaneCore::ConfigItem.new(
key: :set,
env_name: 'BUILDKITE_METADATA_SET',
description: 'The hash of key/value pairs of the meta-data to set',
type: Hash,
optional: true,
default_value: nil
),
FastlaneCore::ConfigItem.new(
key: :get,
env_name: 'BUILDKITE_METADATA_GET',
description: 'The key of the metadata to get the value of',
type: String,
optional: true,
default_value: nil
),
]
end

def self.return_value
'The value of the Buildkite metadata corresponding to the provided `get` key. `nil` if no `get` parameter was provided.'
end

def self.authors
['Automattic']
end

def self.is_supported?(platform)
true
end
end
end
end
82 changes: 82 additions & 0 deletions spec/buildkite_annotate_action_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
require 'spec_helper'

describe Fastlane::Actions::BuildkiteAnnotateAction do
describe '`style` parameter validation' do
it 'errors if we use an invalid style' do
expect(FastlaneCore::UI).to receive(:user_error!).with('Invalid value `failure` for parameter `style`. Valid values are: success, info, warning, error')

run_described_fastlane_action(
context: 'ctx',
style: 'failure',
message: 'Fake message'
)
end

%w[success info warning error].each do |style|
it "accepts `#{style}` as a valid style" do
expect(FastlaneCore::UI).not_to receive(:user_error!)
cmd = run_described_fastlane_action(
context: 'ctx',
style: style,
message: 'message'
)
expect(cmd).to eq("buildkite-agent annotate --context ctx --style #{style} message")
end
end

it 'accepts `nil` as a valid style' do
expect(FastlaneCore::UI).not_to receive(:user_error!)
cmd = run_described_fastlane_action(
context: 'ctx',
message: 'message'
)
expect(cmd).to eq('buildkite-agent annotate --context ctx message')
end
end

describe 'annotation creation' do
it 'generates the right command to create an annotation when message is provided' do
cmd = run_described_fastlane_action(
context: 'ctx',
style: 'warning',
message: 'message'
)
expect(cmd).to eq('buildkite-agent annotate --context ctx --style warning message')
end

it 'properly escapes the message and context' do
cmd = run_described_fastlane_action(
context: 'some ctx',
style: 'warning',
message: 'a <b>nice</b> message; with fun characters & all…'
)
expect(cmd).to eq('buildkite-agent annotate --context some\ ctx --style warning a\ \<b\>nice\</b\>\ message\;\ with\ fun\ characters\ \&\ all\…')
end

it 'falls back to Buildkite\'s default `context` when none is provided' do
cmd = run_described_fastlane_action(
style: 'warning',
message: 'a nice message'
)
expect(cmd).to eq('buildkite-agent annotate --style warning a\ nice\ message')
end

it 'falls back to Buildkite\'s default `style` when none is provided' do
cmd = run_described_fastlane_action(
context: 'my-ctx',
message: 'a nice message'
)
expect(cmd).to eq('buildkite-agent annotate --context my-ctx a\ nice\ message')
end
end

describe 'annotation deletion' do
it 'generates the right command to delete an annotation when no message is provided' do
cmd = run_described_fastlane_action(
context: 'some ctx',
message: nil
)
expect(cmd).to eq('buildkite-agent annotation remove --context some\ ctx || true')
end
end
end
45 changes: 45 additions & 0 deletions spec/buildkite_metadata_action_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require 'spec_helper'

describe Fastlane::Actions::BuildkiteMetadataAction do
it 'calls the right command to set a single metadata' do
expect(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'set', 'foo', 'bar')

res = run_described_fastlane_action(set: { foo: 'bar' })
expect(res).to be_nil
end

it 'calls the commands as many times as necessary when we want to set multiple metadata at once' do
expect(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'set', 'key1', 'value1')
expect(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'set', 'key2', 'value2')

metadata = {
key1: 'value1',
key2: 'value2'
}
run_described_fastlane_action(set: metadata)
end

it 'calls the right command to get the value of metadata, and returns the right value' do
expect(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'get', 'foo')
allow(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'get', 'foo').and_return('foo value')

res = run_described_fastlane_action(get: 'foo')
expect(res).to eq('foo value')
end

it 'allows both setting and getting metadata in the same call' do
# Might not be the main way we intend to use this action… but it's still supported.
expect(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'set', 'key1', 'value1')
expect(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'set', 'key2', 'value2')
expect(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'get', 'key3')
allow(Fastlane::Action).to receive(:sh).with('buildkite-agent', 'meta-data', 'get', 'key3').and_return('value3')

new_metadata = {
key1: 'value1',
key2: 'value2'
}
res = run_described_fastlane_action(set: new_metadata, get: 'key3')

expect(res).to eq('value3')
end
end