Skip to content

Commit 578661e

Browse files
authored
Merge pull request #4 from buildkite-plugins/prep-for-v1
v1 cleanups and fixes
2 parents 84e1ea9 + cf7fbf2 commit 578661e

File tree

21 files changed

+447
-98
lines changed

21 files changed

+447
-98
lines changed

.buildkite/pipeline.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
steps:
2+
- label: ":bash: Plugin"
3+
plugins:
4+
docker-compose#v2.0.0:
5+
run: plugin
6+
- label: ":ruby: Ruby"
7+
plugins:
8+
docker-compose#v2.0.0:
9+
run: ruby
10+
- label: "✨ Lint"
11+
plugins:
12+
plugin-linter#v1.0.0:
13+
name: junit-annotate
14+
- label: ":bash: Shellcheck"
15+
plugins:
16+
shellcheck#v1.0.0:
17+
files: hooks/*

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/tests/tmp

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2017 Buildkite
3+
Copyright (c) 2018 Buildkite
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ steps:
1414
- wait: ~
1515
continue_on_failure: true
1616
- plugins:
17-
junit-annotate#v0.0.1:
17+
junit-annotate#v1.0.0:
1818
artifacts: tmp/junit-*.xml
1919
```
2020
@@ -26,6 +26,30 @@ The artifact glob path to find the JUnit XML files.
2626

2727
Example: `tmp/junit-*.xml`
2828

29+
### `job-uuid-file-pattern` (optional)
30+
31+
The regular expression (with capture group) that matches the job UUID in the junit file names. This is used to create the job links in the annotation.
32+
33+
To use this, configure your test reporter to embed the `$BUILDKITE_JOB_UUID` environment variable into your junit file names. For example `"junit-buildkite-job-$BUILDKITE_JOB_UUID.xml"`.
34+
35+
Default: `-(.*).xml`
36+
37+
## Developing
38+
39+
To test the junit parser (in Ruby) and plugin hooks (in Bash):
40+
41+
```bash
42+
docker-compose run --rm plugin &&
43+
docker-compose run --rm ruby
44+
```
45+
46+
To test the Ruby parser locally:
47+
48+
```bash
49+
cd ruby
50+
rake
51+
```
52+
2953
## License
3054

3155
MIT (see [LICENSE](LICENSE))

docker-compose.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
version: '2'
22
services:
3-
tests:
3+
plugin:
44
image: buildkite/plugin-tester
55
volumes:
66
- ".:/plugin"
7+
depends_on:
8+
- ruby
9+
ruby:
10+
image: ruby:2.5-alpine
11+
command: rake
12+
working_dir: /src
13+
volumes:
14+
- "./ruby:/src"

hooks/command

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,41 @@
22

33
set -euo pipefail
44

5-
junits_dir=$(mktemp -d -p "$(pwd)" "junits-tmp.XXXXXXXXXX")
5+
PLUGIN_DIR="$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)/.."
6+
7+
echo "--- :junit: Download the junits"
8+
9+
artifacts_dir="$(pwd)/$(mktemp -d "junit-annotate-plugin-artifacts-tmp.XXXXXXXXXX")"
10+
annotation_dir="$(pwd)/$(mktemp -d "junit-annotate-plugin-annotation-tmp.XXXXXXXXXX")"
11+
annotation_path="${annotation_dir}/annotation.md"
612

713
function cleanup {
8-
rm -rf "${junits_dir}"
14+
rm -rf "${artifacts_dir}"
15+
rm -rf "${annotation_dir}"
916
}
1017

1118
trap cleanup EXIT
1219

13-
echo "--- :junit: Download the junits"
14-
15-
buildkite-agent artifact download "${BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS}" junits_dir
20+
buildkite-agent artifact download \
21+
"${BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS}" \
22+
"$artifacts_dir"
1623

1724
echo "--- :junit: Processing the junits"
1825

19-
docker run \
20-
--rm \
21-
-v "$(pwd)/src:/usr/src/app" \
22-
-v "${junits_dir}:/junits" \
23-
ruby:2.4 /usr/src/app/run.sh > "${junits_dir}/annotation.md"
24-
25-
cat "${junits_dir}/annotation.md"
26-
27-
echo "--- :buildkite: Creating annotation"
28-
29-
buildkite-agent annotate --context junit --style error < "${junits_dir}/annotation.md"
26+
docker \
27+
--log-level "error" \
28+
run \
29+
--rm \
30+
--volume "$artifacts_dir:/junits" \
31+
--volume "$PLUGIN_DIR/ruby:/src" \
32+
--env "BUILDKITE_PLUGIN_JUNIT_ANNOTATE_JOB_UUID_FILE_PATTERN=${BUILDKITE_PLUGIN_JUNIT_ANNOTATE_JOB_UUID_FILE_PATTERN:-}" \
33+
ruby:2.5-alpine /src/bin/annotate /junits \
34+
> "$annotation_path"
35+
36+
cat "$annotation_path"
37+
38+
if grep -q "<details>" "$annotation_path"; then
39+
echo "--- :buildkite: Creating annotation"
40+
# shellcheck disable=SC2002
41+
cat "$annotation_path" | buildkite-agent annotate --context junit --style error
42+
fi

plugin.json

Lines changed: 0 additions & 7 deletions
This file was deleted.

plugin.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: junit-annotate
2+
description: Annotates your build using JUnit XML reports
3+
author: https://github.com/buildkite
4+
requirements:
5+
- docker
6+
configuration:
7+
properties:
8+
artifacts:
9+
type: string
10+
job-uuid-file-pattern:
11+
type: string
12+
required:
13+
- artifacts
14+
additionalProperties: false

ruby/Rakefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
require "rake/testtask"
2+
3+
Rake::TestTask.new do |t|
4+
t.test_files = FileList['tests/**/*_test.rb']
5+
end
6+
7+
task default: :test

ruby/bin/annotate

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/usr/bin/env ruby
2+
3+
require 'rexml/document'
4+
5+
# Reads a list of junit files and returns a nice Buildkite build annotation on
6+
# STDOUT that summarizes any failures.
7+
8+
junits_dir = ARGV[0]
9+
abort("Usage: annotate <junits-dir>") unless junits_dir
10+
abort("#{junits_dir} does not exist") unless Dir.exist?(junits_dir)
11+
12+
job_pattern = ENV['BUILDKITE_PLUGIN_JUNIT_ANNOTATE_JOB_UUID_FILE_PATTERN']
13+
job_pattern = '-(.*).xml' if !job_pattern || job_pattern.empty?
14+
15+
class Failure < Struct.new(:name, :classname, :body, :job); end
16+
17+
junit_report_files = Dir.glob(File.join(junits_dir, "**", "*"))
18+
all_failures = []
19+
20+
junit_report_files.each do |file|
21+
next if File.directory?(file)
22+
23+
STDERR.puts "Parsing #{file.sub(junits_dir, '')}"
24+
job = File.basename(file)[/#{job_pattern}/, 1]
25+
xml = File.read(file)
26+
doc = REXML::Document.new(xml)
27+
28+
doc.elements.each('*/testcase') do |testcase|
29+
name = testcase.attributes['name'].to_s
30+
classname = testcase.attributes['classname'].to_s
31+
testcase.elements.each("failure") do |failure|
32+
all_failures << Failure.new(name, classname, failure.text.chomp.strip, job)
33+
end
34+
end
35+
end
36+
37+
STDERR.puts "--- ❓ Checking failures"
38+
39+
if all_failures.empty?
40+
STDERR.puts "There were no failures 🙌"
41+
exit 0
42+
else
43+
STDERR.puts "There are #{all_failures.length} failures 😭"
44+
end
45+
46+
STDERR.puts "--- ✍️ Preparing annotation"
47+
48+
puts "There were #{all_failures.length} failures:\n\n"
49+
50+
all_failures.each do |failure|
51+
puts "<details>"
52+
puts "<summary><code>#{failure.name} in #{failure.classname}</code></summary>\n\n"
53+
puts "<code><pre>#{failure.body}</pre></code>\n\n"
54+
if failure.job
55+
puts "in <a href=\"##{failure.job}\">Job ##{failure.job}</a>"
56+
end
57+
puts "</details>"
58+
puts "" unless failure == all_failures.last
59+
end

0 commit comments

Comments
 (0)