elastic-apm 3.3.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ci/.jenkins_exclude.yml +43 -24
- data/.ci/.jenkins_framework.yml +2 -2
- data/.ci/.jenkins_ruby.yml +1 -1
- data/.ci/Jenkinsfile +288 -170
- data/.ci/docker/jruby/11-jdk/Dockerfile +40 -0
- data/.ci/docker/jruby/12-jdk/Dockerfile +40 -0
- data/.ci/docker/jruby/13-jdk/Dockerfile +40 -0
- data/.ci/docker/jruby/7-jdk/Dockerfile +40 -0
- data/.ci/docker/jruby/8-jdk/Dockerfile +40 -0
- data/.ci/docker/jruby/README.md +31 -0
- data/.ci/docker/jruby/run.sh +73 -0
- data/.ci/docker/jruby/test.sh +13 -0
- data/.ci/jobs/apm-agent-ruby-downstream.yml +1 -0
- data/.ci/jobs/apm-agent-ruby-linting-mbp.yml +1 -0
- data/.ci/jobs/apm-agent-ruby-mbp.yml +1 -0
- data/.ci/jobs/defaults.yml +1 -1
- data/.ci/packer_cache.sh +16 -0
- data/.github/ISSUE_TEMPLATE/Bug_report.md +40 -0
- data/.github/ISSUE_TEMPLATE/Feature_request.md +17 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +60 -0
- data/.gitignore +8 -0
- data/.rspec +0 -1
- data/.rubocop.yml +18 -0
- data/CHANGELOG.asciidoc +104 -1
- data/CONTRIBUTING.md +6 -48
- data/Gemfile +38 -10
- data/README.md +62 -13
- data/Rakefile +37 -5
- data/bench/sql.rb +49 -0
- data/bin/build_docs +1 -1
- data/bin/run-tests +4 -1
- data/docker-compose.yml +7 -0
- data/docs/api.asciidoc +52 -3
- data/docs/configuration.asciidoc +171 -22
- data/docs/getting-started-rails.asciidoc +2 -0
- data/docs/graphql.asciidoc +23 -0
- data/docs/images/dynamic-config.svg +1 -0
- data/docs/index.asciidoc +6 -1
- data/docs/introduction.asciidoc +2 -1
- data/docs/performance-tuning.asciidoc +106 -0
- data/docs/set-up.asciidoc +5 -2
- data/docs/supported-technologies.asciidoc +86 -1
- data/docs/upgrading.asciidoc +45 -0
- data/elastic-apm.gemspec +17 -0
- data/lib/elastic-apm.rb +17 -0
- data/lib/elastic_apm.rb +58 -5
- data/lib/elastic_apm/agent.rb +55 -4
- data/lib/elastic_apm/central_config.rb +27 -8
- data/lib/elastic_apm/central_config/cache_control.rb +17 -0
- data/lib/elastic_apm/child_durations.rb +26 -4
- data/lib/elastic_apm/config.rb +86 -5
- data/lib/elastic_apm/config/bytes.rb +17 -0
- data/lib/elastic_apm/config/duration.rb +17 -0
- data/lib/elastic_apm/config/options.rb +21 -5
- data/lib/elastic_apm/config/regexp_list.rb +17 -0
- data/lib/elastic_apm/config/wildcard_pattern_list.rb +20 -1
- data/lib/elastic_apm/context.rb +17 -0
- data/lib/elastic_apm/context/request.rb +17 -0
- data/lib/elastic_apm/context/request/socket.rb +17 -0
- data/lib/elastic_apm/context/request/url.rb +17 -0
- data/lib/elastic_apm/context/response.rb +27 -2
- data/lib/elastic_apm/context/user.rb +17 -0
- data/lib/elastic_apm/context_builder.rb +17 -0
- data/lib/elastic_apm/deprecations.rb +17 -0
- data/lib/elastic_apm/error.rb +17 -0
- data/lib/elastic_apm/error/exception.rb +17 -0
- data/lib/elastic_apm/error/log.rb +17 -0
- data/lib/elastic_apm/error_builder.rb +17 -0
- data/lib/elastic_apm/grape.rb +17 -0
- data/lib/elastic_apm/graphql.rb +91 -0
- data/lib/elastic_apm/grpc.rb +99 -0
- data/lib/elastic_apm/instrumenter.rb +49 -15
- data/lib/elastic_apm/internal_error.rb +17 -0
- data/lib/elastic_apm/logging.rb +17 -0
- data/lib/elastic_apm/metadata.rb +17 -0
- data/lib/elastic_apm/metadata/process_info.rb +17 -0
- data/lib/elastic_apm/metadata/service_info.rb +22 -2
- data/lib/elastic_apm/metadata/system_info.rb +17 -0
- data/lib/elastic_apm/metadata/system_info/container_info.rb +17 -0
- data/lib/elastic_apm/metrics.rb +33 -1
- data/lib/elastic_apm/metrics/breakdown_set.rb +17 -0
- data/lib/elastic_apm/metrics/cpu_mem_set.rb +17 -0
- data/lib/elastic_apm/metrics/metric.rb +23 -4
- data/lib/elastic_apm/metrics/set.rb +17 -0
- data/lib/elastic_apm/metrics/span_scoped_set.rb +17 -0
- data/lib/elastic_apm/metrics/transaction_set.rb +17 -0
- data/lib/elastic_apm/metrics/vm_set.rb +17 -0
- data/lib/elastic_apm/metricset.rb +17 -0
- data/lib/elastic_apm/middleware.rb +20 -4
- data/lib/elastic_apm/naively_hashable.rb +17 -0
- data/lib/elastic_apm/normalizers.rb +17 -0
- data/lib/elastic_apm/normalizers/grape.rb +17 -0
- data/lib/elastic_apm/normalizers/grape/endpoint_run.rb +18 -1
- data/lib/elastic_apm/normalizers/rails.rb +17 -0
- data/lib/elastic_apm/normalizers/rails/action_controller.rb +17 -0
- data/lib/elastic_apm/normalizers/rails/action_mailer.rb +17 -0
- data/lib/elastic_apm/normalizers/rails/action_view.rb +17 -0
- data/lib/elastic_apm/normalizers/rails/active_record.rb +29 -5
- data/lib/elastic_apm/opentracing.rb +66 -24
- data/lib/elastic_apm/rails.rb +18 -10
- data/lib/elastic_apm/railtie.rb +18 -1
- data/lib/elastic_apm/resque.rb +29 -0
- data/lib/elastic_apm/sinatra.rb +17 -0
- data/lib/elastic_apm/span.rb +20 -2
- data/lib/elastic_apm/span/context.rb +43 -44
- data/lib/elastic_apm/span/context/db.rb +43 -0
- data/lib/elastic_apm/span/context/destination.rb +77 -0
- data/lib/elastic_apm/span/context/http.rb +43 -0
- data/lib/elastic_apm/span_helpers.rb +17 -0
- data/lib/elastic_apm/spies.rb +33 -14
- data/lib/elastic_apm/spies/action_dispatch.rb +17 -0
- data/lib/elastic_apm/spies/delayed_job.rb +17 -0
- data/lib/elastic_apm/spies/elasticsearch.rb +49 -5
- data/lib/elastic_apm/spies/faraday.rb +53 -18
- data/lib/elastic_apm/spies/http.rb +35 -3
- data/lib/elastic_apm/spies/json.rb +17 -0
- data/lib/elastic_apm/spies/mongo.rb +23 -1
- data/lib/elastic_apm/spies/net_http.rb +50 -8
- data/lib/elastic_apm/spies/rake.rb +17 -0
- data/lib/elastic_apm/spies/redis.rb +17 -0
- data/lib/elastic_apm/spies/resque.rb +53 -0
- data/lib/elastic_apm/spies/sequel.rb +52 -15
- data/lib/elastic_apm/spies/shoryuken.rb +65 -0
- data/lib/elastic_apm/spies/sidekiq.rb +17 -0
- data/lib/elastic_apm/spies/sinatra.rb +17 -0
- data/lib/elastic_apm/spies/sneakers.rb +74 -0
- data/lib/elastic_apm/spies/sucker_punch.rb +54 -0
- data/lib/elastic_apm/spies/tilt.rb +17 -0
- data/lib/elastic_apm/sql.rb +36 -0
- data/lib/elastic_apm/sql/signature.rb +169 -0
- data/lib/elastic_apm/sql/tokenizer.rb +264 -0
- data/lib/elastic_apm/sql/tokens.rb +63 -0
- data/lib/elastic_apm/sql_summarizer.rb +18 -2
- data/lib/elastic_apm/stacktrace.rb +17 -0
- data/lib/elastic_apm/stacktrace/frame.rb +17 -0
- data/lib/elastic_apm/stacktrace_builder.rb +23 -1
- data/lib/elastic_apm/subscriber.rb +17 -0
- data/lib/elastic_apm/trace_context.rb +85 -49
- data/lib/elastic_apm/trace_context/traceparent.rb +113 -0
- data/lib/elastic_apm/trace_context/tracestate.rb +43 -0
- data/lib/elastic_apm/transaction.rb +43 -15
- data/lib/elastic_apm/transport/base.rb +39 -23
- data/lib/elastic_apm/transport/connection.rb +17 -0
- data/lib/elastic_apm/transport/connection/http.rb +17 -0
- data/lib/elastic_apm/transport/connection/proxy_pipe.rb +24 -2
- data/lib/elastic_apm/transport/filters.rb +17 -0
- data/lib/elastic_apm/transport/filters/hash_sanitizer.rb +77 -0
- data/lib/elastic_apm/transport/filters/secrets_filter.rb +29 -53
- data/lib/elastic_apm/transport/headers.rb +21 -0
- data/lib/elastic_apm/transport/serializers.rb +17 -0
- data/lib/elastic_apm/transport/serializers/context_serializer.rb +17 -0
- data/lib/elastic_apm/transport/serializers/error_serializer.rb +17 -0
- data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +44 -20
- data/lib/elastic_apm/transport/serializers/metricset_serializer.rb +17 -0
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +45 -8
- data/lib/elastic_apm/transport/serializers/transaction_serializer.rb +17 -0
- data/lib/elastic_apm/transport/user_agent.rb +17 -0
- data/lib/elastic_apm/transport/worker.rb +17 -0
- data/lib/elastic_apm/util.rb +17 -0
- data/lib/elastic_apm/util/inflector.rb +17 -0
- data/lib/elastic_apm/util/lru_cache.rb +17 -0
- data/lib/elastic_apm/util/throttle.rb +17 -0
- data/lib/elastic_apm/version.rb +18 -1
- metadata +36 -7
- data/.ci/downstreamTests.groovy +0 -185
- data/.ci/prepare-git-context.sh +0 -24
- data/.github/workflows/main.yml +0 -14
- data/CHANGELOG.md +0 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 32d176ac5c22246df60ecc92f1baad37011a872af4e64f842b5a43c699a6ced3
|
|
4
|
+
data.tar.gz: bf0505e728d8f1bd0bb941574a844a8990d14e13a94a7c9f2a6c2d0f9eb5d35b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d57b6bdbc7e78bea95e197d33f3eef4db7a35b23f9e450c636c3c1e3a5dd75ee8ee195709944ac51d6af7a3ab2451fd19d6139f0dedf9e86a4827732d5097f2a
|
|
7
|
+
data.tar.gz: 924b9bbf782a0fa4e376137e9a44d10cc01be71455ca7614ab1374c70f0926d174eb1078b985cd60c1693e2e0ec1cdd8446502bd40007359262dcc31241250aa
|
data/.ci/.jenkins_exclude.yml
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
exclude:
|
|
2
|
-
|
|
2
|
+
# Ruby 2.3
|
|
3
|
+
# Only includes rails-5.2, sinatra-2.0
|
|
4
|
+
- RUBY_VERSION: ruby:2.3
|
|
5
|
+
FRAMEWORK: rails-6.0
|
|
6
|
+
- RUBY_VERSION: ruby:2.3
|
|
7
|
+
FRAMEWORK: rails-5.1
|
|
8
|
+
- RUBY_VERSION: ruby:2.3
|
|
9
|
+
FRAMEWORK: rails-5.0
|
|
10
|
+
- RUBY_VERSION: ruby:2.3
|
|
11
|
+
FRAMEWORK: rails-4.2
|
|
12
|
+
- RUBY_VERSION: ruby:2.3
|
|
13
|
+
FRAMEWORK: sinatra-1.4
|
|
14
|
+
- RUBY_VERSION: ruby:2.3
|
|
15
|
+
FRAMEWORK: grape-1.3
|
|
16
|
+
- RUBY_VERSION: ruby:2.3
|
|
17
|
+
FRAMEWORK: grape-1.3,sinatra-2.0,rails-6.0
|
|
18
|
+
- RUBY_VERSION: ruby:2.3
|
|
19
|
+
FRAMEWORK: rails-master
|
|
20
|
+
- RUBY_VERSION: ruby:2.3
|
|
21
|
+
FRAMEWORK: sinatra-master
|
|
22
|
+
- RUBY_VERSION: ruby:2.3
|
|
23
|
+
FRAMEWORK: grape-master
|
|
24
|
+
|
|
25
|
+
- RUBY_VERSION: ruby:2.7
|
|
3
26
|
FRAMEWORK: rails-4.2
|
|
4
27
|
- RUBY_VERSION: jruby:9.2
|
|
5
28
|
FRAMEWORK: rails-4.2
|
|
@@ -18,22 +41,18 @@ exclude:
|
|
|
18
41
|
|
|
19
42
|
- RUBY_VERSION: ruby:2.4
|
|
20
43
|
FRAMEWORK: rails-6.0
|
|
21
|
-
- RUBY_VERSION: ruby:2.3
|
|
22
|
-
FRAMEWORK: rails-6.0
|
|
23
44
|
- RUBY_VERSION: jruby:9.1
|
|
24
45
|
FRAMEWORK: rails-6.0
|
|
25
46
|
- RUBY_VERSION: docker.elastic.co/observability-ci/jruby:9.1-7-jdk
|
|
26
47
|
FRAMEWORK: rails-6.0
|
|
27
48
|
|
|
28
49
|
# Only test master on newest ruby
|
|
29
|
-
- RUBY_VERSION: ruby:2.
|
|
50
|
+
- RUBY_VERSION: ruby:2.6
|
|
30
51
|
FRAMEWORK: rails-master
|
|
31
52
|
- RUBY_VERSION: ruby:2.5
|
|
32
53
|
FRAMEWORK: rails-master
|
|
33
54
|
- RUBY_VERSION: ruby:2.4
|
|
34
55
|
FRAMEWORK: rails-master
|
|
35
|
-
- RUBY_VERSION: ruby:2.3
|
|
36
|
-
FRAMEWORK: rails-master
|
|
37
56
|
- RUBY_VERSION: jruby:9.2
|
|
38
57
|
FRAMEWORK: rails-master
|
|
39
58
|
- RUBY_VERSION: jruby:9.1
|
|
@@ -53,8 +72,6 @@ exclude:
|
|
|
53
72
|
FRAMEWORK: sinatra-master
|
|
54
73
|
- RUBY_VERSION: ruby:2.4
|
|
55
74
|
FRAMEWORK: sinatra-master
|
|
56
|
-
- RUBY_VERSION: ruby:2.3
|
|
57
|
-
FRAMEWORK: sinatra-master
|
|
58
75
|
- RUBY_VERSION: jruby:9.2
|
|
59
76
|
FRAMEWORK: sinatra-master
|
|
60
77
|
- RUBY_VERSION: jruby:9.1
|
|
@@ -70,14 +87,12 @@ exclude:
|
|
|
70
87
|
- RUBY_VERSION: docker.elastic.co/observability-ci/jruby:9.1-7-jdk
|
|
71
88
|
FRAMEWORK: sinatra-master
|
|
72
89
|
|
|
73
|
-
- RUBY_VERSION: ruby:2.
|
|
90
|
+
- RUBY_VERSION: ruby:2.6
|
|
74
91
|
FRAMEWORK: grape-master
|
|
75
92
|
- RUBY_VERSION: ruby:2.5
|
|
76
93
|
FRAMEWORK: grape-master
|
|
77
94
|
- RUBY_VERSION: ruby:2.4
|
|
78
95
|
FRAMEWORK: grape-master
|
|
79
|
-
- RUBY_VERSION: ruby:2.3
|
|
80
|
-
FRAMEWORK: grape-master
|
|
81
96
|
- RUBY_VERSION: jruby:9.2
|
|
82
97
|
FRAMEWORK: grape-master
|
|
83
98
|
- RUBY_VERSION: jruby:9.1
|
|
@@ -93,25 +108,29 @@ exclude:
|
|
|
93
108
|
- RUBY_VERSION: docker.elastic.co/observability-ci/jruby:9.1-7-jdk
|
|
94
109
|
FRAMEWORK: grape-master
|
|
95
110
|
|
|
96
|
-
|
|
97
|
-
|
|
111
|
+
# Unsupported
|
|
112
|
+
- RUBY_VERSION: jruby:9.1
|
|
113
|
+
FRAMEWORK: grape-1.3
|
|
114
|
+
- RUBY_VERSION: docker.elastic.co/observability-ci/jruby:9.1-7-jdk
|
|
115
|
+
FRAMEWORK: grape-1.3
|
|
116
|
+
|
|
117
|
+
- RUBY_VERSION: ruby:2.6
|
|
118
|
+
FRAMEWORK: grape-1.3,sinatra-2.0,rails-6.0
|
|
98
119
|
- RUBY_VERSION: ruby:2.5
|
|
99
|
-
FRAMEWORK: grape-1.
|
|
120
|
+
FRAMEWORK: grape-1.3,sinatra-2.0,rails-6.0
|
|
100
121
|
- RUBY_VERSION: ruby:2.4
|
|
101
|
-
FRAMEWORK: grape-1.
|
|
102
|
-
- RUBY_VERSION: ruby:2.3
|
|
103
|
-
FRAMEWORK: grape-1.2,sinatra-2.0,rails-6.0
|
|
122
|
+
FRAMEWORK: grape-1.3,sinatra-2.0,rails-6.0
|
|
104
123
|
- RUBY_VERSION: jruby:9.2
|
|
105
|
-
FRAMEWORK: grape-1.
|
|
124
|
+
FRAMEWORK: grape-1.3,sinatra-2.0,rails-6.0
|
|
106
125
|
- RUBY_VERSION: jruby:9.1
|
|
107
|
-
FRAMEWORK: grape-1.
|
|
126
|
+
FRAMEWORK: grape-1.3,sinatra-2.0,rails-6.0
|
|
108
127
|
- RUBY_VERSION: docker.elastic.co/observability-ci/jruby:9.2-13-jdk
|
|
109
|
-
FRAMEWORK: grape-1.
|
|
128
|
+
FRAMEWORK: grape-1.3,sinatra-2.0,rails-6.0
|
|
110
129
|
- RUBY_VERSION: docker.elastic.co/observability-ci/jruby:9.2-12-jdk
|
|
111
|
-
FRAMEWORK: grape-1.
|
|
130
|
+
FRAMEWORK: grape-1.3,sinatra-2.0,rails-6.0
|
|
112
131
|
- RUBY_VERSION: docker.elastic.co/observability-ci/jruby:9.2-11-jdk
|
|
113
|
-
FRAMEWORK: grape-1.
|
|
132
|
+
FRAMEWORK: grape-1.3,sinatra-2.0,rails-6.0
|
|
114
133
|
- RUBY_VERSION: docker.elastic.co/observability-ci/jruby:9.2-8-jdk
|
|
115
|
-
FRAMEWORK: grape-1.
|
|
134
|
+
FRAMEWORK: grape-1.3,sinatra-2.0,rails-6.0
|
|
116
135
|
- RUBY_VERSION: docker.elastic.co/observability-ci/jruby:9.1-7-jdk
|
|
117
|
-
FRAMEWORK: grape-1.
|
|
136
|
+
FRAMEWORK: grape-1.3,sinatra-2.0,rails-6.0
|
data/.ci/.jenkins_framework.yml
CHANGED
data/.ci/.jenkins_ruby.yml
CHANGED
data/.ci/Jenkinsfile
CHANGED
|
@@ -5,16 +5,17 @@ import co.elastic.matrix.*
|
|
|
5
5
|
import groovy.transform.Field
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
|
|
8
|
+
This is the parallel tasks generator,
|
|
9
|
+
it is need as field to store the results of the tests.
|
|
9
10
|
*/
|
|
10
|
-
@Field def
|
|
11
|
+
@Field def rubyTasksGen
|
|
11
12
|
|
|
12
13
|
pipeline {
|
|
13
14
|
agent { label 'linux && immutable' }
|
|
14
15
|
environment {
|
|
15
16
|
REPO = 'apm-agent-ruby'
|
|
16
17
|
BASE_DIR = "src/github.com/elastic/${env.REPO}"
|
|
17
|
-
PIPELINE_LOG_LEVEL='
|
|
18
|
+
PIPELINE_LOG_LEVEL = 'INFO'
|
|
18
19
|
NOTIFY_TO = credentials('notify-to')
|
|
19
20
|
JOB_GCS_BUCKET = credentials('gcs-bucket')
|
|
20
21
|
CODECOV_SECRET = 'secret/apm-team/ci/apm-agent-ruby-codecov'
|
|
@@ -24,6 +25,7 @@ pipeline {
|
|
|
24
25
|
ITS_PIPELINE = 'apm-integration-tests-selector-mbp/master'
|
|
25
26
|
RELEASE_SECRET = 'secret/apm-team/ci/apm-agent-ruby-rubygems-release'
|
|
26
27
|
OPBEANS_REPO = 'opbeans-ruby'
|
|
28
|
+
REFERENCE_REPO = '/var/lib/jenkins/.git-references/apm-agent-ruby.git'
|
|
27
29
|
}
|
|
28
30
|
options {
|
|
29
31
|
timeout(time: 2, unit: 'HOURS')
|
|
@@ -36,7 +38,7 @@ pipeline {
|
|
|
36
38
|
quietPeriod(10)
|
|
37
39
|
}
|
|
38
40
|
triggers {
|
|
39
|
-
issueCommentTrigger('(?i).*(?:jenkins\\W+)?run\\W+(?:the\\W+)?tests(?:\\W+please)?.*')
|
|
41
|
+
issueCommentTrigger('(?i).*(?:jenkins\\W+)?run\\W+(?:the\\W+)?(?:benchmark\\W+)?tests(?:\\W+please)?.*')
|
|
40
42
|
}
|
|
41
43
|
parameters {
|
|
42
44
|
booleanParam(name: 'Run_As_Master_Branch', defaultValue: false, description: 'Allow to run any steps on a PR, some steps normally only run on master branch.')
|
|
@@ -51,16 +53,25 @@ pipeline {
|
|
|
51
53
|
steps {
|
|
52
54
|
pipelineManager([ cancelPreviousRunningBuilds: [ when: 'PR' ] ])
|
|
53
55
|
deleteDir()
|
|
54
|
-
gitCheckout(basedir: "${BASE_DIR}", githubNotifyFirstTimeContributor: true)
|
|
56
|
+
gitCheckout(basedir: "${BASE_DIR}", githubNotifyFirstTimeContributor: true, reference: "${env.REFERENCE_REPO}")
|
|
55
57
|
stash allowEmpty: true, name: 'source', useDefaultExcludes: false
|
|
58
|
+
script {
|
|
59
|
+
dir("${BASE_DIR}"){
|
|
60
|
+
// Skip all the stages except docs for PR's with asciidoc and md changes only
|
|
61
|
+
env.ONLY_DOCS = isGitRegionMatch(patterns: [ '.*\\.(asciidoc|md)' ], shouldMatchAll: true)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
56
64
|
}
|
|
57
65
|
}
|
|
58
66
|
stage('Sanity checks') {
|
|
59
67
|
when {
|
|
60
68
|
beforeAgent true
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
allOf {
|
|
70
|
+
expression { return env.ONLY_DOCS == "false" }
|
|
71
|
+
anyOf {
|
|
72
|
+
not { changeRequest() }
|
|
73
|
+
expression { return params.Run_As_Master_Branch }
|
|
74
|
+
}
|
|
64
75
|
}
|
|
65
76
|
}
|
|
66
77
|
options { skipDefaultCheckout() }
|
|
@@ -83,168 +94,177 @@ pipeline {
|
|
|
83
94
|
/**
|
|
84
95
|
Execute unit tests.
|
|
85
96
|
*/
|
|
86
|
-
stage('
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
parallel(testTasks)
|
|
100
|
-
}
|
|
97
|
+
stage('Tests') {
|
|
98
|
+
when {
|
|
99
|
+
beforeAgent true
|
|
100
|
+
expression { return env.ONLY_DOCS == "false" }
|
|
101
|
+
}
|
|
102
|
+
failFast false
|
|
103
|
+
parallel {
|
|
104
|
+
stage('Tests') {
|
|
105
|
+
options { skipDefaultCheckout() }
|
|
106
|
+
steps {
|
|
107
|
+
withGithubNotify(context: 'Tests', tab: 'tests') {
|
|
108
|
+
runTests('.ci/.jenkins_framework.yml')
|
|
101
109
|
}
|
|
102
110
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
options { skipDefaultCheckout() }
|
|
107
|
-
when {
|
|
108
|
-
beforeAgent true
|
|
109
|
-
allOf {
|
|
110
|
-
anyOf {
|
|
111
|
-
branch 'master'
|
|
112
|
-
branch "\\d+\\.\\d+"
|
|
113
|
-
branch "v\\d?"
|
|
114
|
-
tag "v\\d+\\.\\d+\\.\\d+*"
|
|
115
|
-
expression { return params.Run_As_Master_Branch }
|
|
111
|
+
post {
|
|
112
|
+
always {
|
|
113
|
+
convergeCoverage()
|
|
116
114
|
}
|
|
117
|
-
expression { return params.bench_ci }
|
|
118
115
|
}
|
|
119
116
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
post {
|
|
127
|
-
always {
|
|
128
|
-
cleanWs()
|
|
129
|
-
}
|
|
117
|
+
stage('Master Tests frameworks') {
|
|
118
|
+
options { skipDefaultCheckout() }
|
|
119
|
+
steps {
|
|
120
|
+
catchError(buildResult: 'SUCCESS', stageResult: 'UNSTABLE', message: "The tests for the master framework have failed. Let's warn instead.") {
|
|
121
|
+
runTests('.ci/.jenkins_master_framework.yml')
|
|
130
122
|
}
|
|
131
123
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
unstash 'source'
|
|
141
|
-
dir("${BASE_DIR}"){
|
|
142
|
-
script {
|
|
143
|
-
def versions = readYaml(file: ".ci/.jenkins_ruby.yml")
|
|
144
|
-
def benchmarkTask = [:]
|
|
145
|
-
// TODO: benchmark for the jruby:9.2 and similar versions got some issues with
|
|
146
|
-
// NoMethodError: undefined method `[]' for nil:NilClass
|
|
147
|
-
// <main> at bench/report.rb:48
|
|
148
|
-
versions['RUBY_VERSION'].findAll { !it.contains('9.2') }.each{ v ->
|
|
149
|
-
benchmarkTask[v] = runBenchmark(v)
|
|
150
|
-
}
|
|
151
|
-
parallel(benchmarkTask)
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
124
|
+
}
|
|
125
|
+
stage('Integration Tests') {
|
|
126
|
+
agent none
|
|
127
|
+
when {
|
|
128
|
+
beforeAgent true
|
|
129
|
+
anyOf {
|
|
130
|
+
changeRequest()
|
|
131
|
+
expression { return !params.Run_As_Master_Branch }
|
|
155
132
|
}
|
|
156
133
|
}
|
|
134
|
+
steps {
|
|
135
|
+
githubNotify(context: "${env.GITHUB_CHECK_ITS_NAME}", description: "${env.GITHUB_CHECK_ITS_NAME} ...", status: 'PENDING', targetUrl: "${env.JENKINS_URL}search/?q=${env.ITS_PIPELINE.replaceAll('/','+')}")
|
|
136
|
+
build(job: env.ITS_PIPELINE, propagate: false, wait: true,
|
|
137
|
+
parameters: [ string(name: 'INTEGRATION_TEST', value: 'Ruby'),
|
|
138
|
+
string(name: 'BUILD_OPTS', value: "--ruby-agent-version ${env.GIT_BASE_COMMIT} --ruby-agent-version-state ${env.GIT_BUILD_CAUSE} --ruby-agent-repo ${env.CHANGE_FORK?.trim() ?: 'elastic'}/${env.REPO} --opbeans-ruby-agent-branch ${env.GIT_BASE_COMMIT}"),
|
|
139
|
+
string(name: 'GITHUB_CHECK_NAME', value: env.GITHUB_CHECK_ITS_NAME),
|
|
140
|
+
string(name: 'GITHUB_CHECK_REPO', value: env.REPO),
|
|
141
|
+
string(name: 'GITHUB_CHECK_SHA1', value: env.GIT_BASE_COMMIT) ])
|
|
142
|
+
}
|
|
157
143
|
}
|
|
158
144
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
stage('Integration Tests') {
|
|
168
|
-
agent none
|
|
169
|
-
when {
|
|
170
|
-
beforeAgent true
|
|
145
|
+
}
|
|
146
|
+
stage('Benchmarks') {
|
|
147
|
+
options { skipDefaultCheckout() }
|
|
148
|
+
when {
|
|
149
|
+
beforeAgent true
|
|
150
|
+
allOf {
|
|
171
151
|
anyOf {
|
|
172
|
-
|
|
173
|
-
|
|
152
|
+
branch 'master'
|
|
153
|
+
branch "\\d+\\.\\d+"
|
|
154
|
+
branch "v\\d?"
|
|
155
|
+
tag pattern: 'v\\d+.*', comparator: "REGEXP"
|
|
156
|
+
expression { return params.Run_As_Master_Branch }
|
|
157
|
+
expression { return env.GITHUB_COMMENT?.contains('benchmark tests') }
|
|
174
158
|
}
|
|
175
|
-
|
|
176
|
-
steps {
|
|
177
|
-
log(level: 'INFO', text: 'Launching Async ITs')
|
|
178
|
-
build(job: env.ITS_PIPELINE, propagate: false, wait: false,
|
|
179
|
-
parameters: [string(name: 'AGENT_INTEGRATION_TEST', value: 'Ruby'),
|
|
180
|
-
string(name: 'BUILD_OPTS', value: "--ruby-agent-version ${env.GIT_BASE_COMMIT} --ruby-agent-version-state ${env.GIT_BUILD_CAUSE} --ruby-agent-repo ${env.CHANGE_FORK?.trim() ?: 'elastic'}/${env.REPO}"),
|
|
181
|
-
string(name: 'GITHUB_CHECK_NAME', value: env.GITHUB_CHECK_ITS_NAME),
|
|
182
|
-
string(name: 'GITHUB_CHECK_REPO', value: env.REPO),
|
|
183
|
-
string(name: 'GITHUB_CHECK_SHA1', value: env.GIT_BASE_COMMIT)]
|
|
184
|
-
)
|
|
185
|
-
githubNotify(context: "${env.GITHUB_CHECK_ITS_NAME}", description: "${env.GITHUB_CHECK_ITS_NAME} ...", status: 'PENDING', targetUrl: "${env.JENKINS_URL}search/?q=${env.ITS_PIPELINE.replaceAll('/','+')}")
|
|
159
|
+
expression { return params.bench_ci }
|
|
186
160
|
}
|
|
187
161
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
162
|
+
stages {
|
|
163
|
+
stage('Clean Workspace') {
|
|
164
|
+
agent { label 'metal' }
|
|
165
|
+
steps {
|
|
166
|
+
echo "Cleaning Workspace"
|
|
167
|
+
}
|
|
168
|
+
post {
|
|
169
|
+
always {
|
|
170
|
+
cleanWs()
|
|
171
|
+
}
|
|
172
|
+
}
|
|
197
173
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
174
|
+
/**
|
|
175
|
+
Run the benchmarks and store the results on ES.
|
|
176
|
+
The result JSON files are also archive into Jenkins.
|
|
177
|
+
*/
|
|
178
|
+
stage('Run Benchmarks') {
|
|
179
|
+
steps {
|
|
180
|
+
withGithubNotify(context: 'Run Benchmarks') {
|
|
181
|
+
deleteDir()
|
|
182
|
+
unstash 'source'
|
|
183
|
+
dir("${BASE_DIR}"){
|
|
204
184
|
script {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
sshagent(['f6c7695a-671e-4f4f-a331-acdce44ff9ba']) {
|
|
210
|
-
sh '.ci/prepare-git-context.sh'
|
|
211
|
-
sh 'gem install rake yard rspec'
|
|
212
|
-
sh 'rake release'
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
185
|
+
def versions = readYaml(file: ".ci/.jenkins_ruby.yml")
|
|
186
|
+
def benchmarkTask = [:]
|
|
187
|
+
versions['RUBY_VERSION'].each{ v ->
|
|
188
|
+
benchmarkTask[v] = runBenchmark(v)
|
|
217
189
|
}
|
|
190
|
+
parallel(benchmarkTask)
|
|
218
191
|
}
|
|
219
192
|
}
|
|
220
193
|
}
|
|
221
194
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
stage('Release') {
|
|
199
|
+
options { skipDefaultCheckout() }
|
|
200
|
+
environment {
|
|
201
|
+
RUBY_DOCKER_TAG = 'ruby:2.6'
|
|
202
|
+
HOME = '/var/lib/jenkins'
|
|
203
|
+
}
|
|
204
|
+
when {
|
|
205
|
+
beforeAgent true
|
|
206
|
+
tag pattern: 'v\\d+.*', comparator: 'REGEXP'
|
|
207
|
+
}
|
|
208
|
+
stages {
|
|
209
|
+
stage('Release') {
|
|
210
|
+
steps {
|
|
211
|
+
deleteDir()
|
|
212
|
+
unstash 'source'
|
|
213
|
+
prepareRelease(){
|
|
214
|
+
sh 'rake release'
|
|
225
215
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
gitCreateTag(tag: "${env.BRANCH_NAME}")
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
stage('Update Branch') {
|
|
219
|
+
steps {
|
|
220
|
+
deleteDir()
|
|
221
|
+
unstash 'source'
|
|
222
|
+
prepareRelease(){
|
|
223
|
+
catchError(buildResult: 'SUCCESS', message: 'Update branch task failed', stageResult: 'UNSTABLE') {
|
|
224
|
+
sh 'rake release:update_branch'
|
|
236
225
|
}
|
|
237
226
|
}
|
|
238
227
|
}
|
|
228
|
+
post {
|
|
229
|
+
unsuccessful {
|
|
230
|
+
emailext subject: "[${env.REPO}] Syncup post-release stage failed.", to: "${NOTIFY_TO}",
|
|
231
|
+
body: "Please go to ${env.BUILD_URL} to review the logs. Most likely you need to update the branch manually."
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
stage('Opbeans') {
|
|
236
|
+
environment {
|
|
237
|
+
REPO_NAME = "${OPBEANS_REPO}"
|
|
238
|
+
VERSION = "${env.BRANCH_NAME.replaceAll('^v', '')}"
|
|
239
|
+
}
|
|
240
|
+
steps {
|
|
241
|
+
deleteDir()
|
|
242
|
+
// Let's wait for the Gem to be available
|
|
243
|
+
sh label: 'Wait for gem', script: """#!/usr/bin/env bash
|
|
244
|
+
source /usr/local/bin/bash_standard_lib.sh
|
|
245
|
+
(retry 10 curl --silent --show-error --fail -I https://rubygems.org/gems/elastic-apm/versions/${env.VERSION})
|
|
246
|
+
"""
|
|
247
|
+
dir("${OPBEANS_REPO}"){
|
|
248
|
+
git credentialsId: 'f6c7695a-671e-4f4f-a331-acdce44ff9ba',
|
|
249
|
+
url: "git@github.com:elastic/${OPBEANS_REPO}.git"
|
|
250
|
+
// It's required to transform the tag value to the gem version
|
|
251
|
+
sh script: ".ci/bump-version.sh ${env.VERSION}", label: 'Bump version'
|
|
252
|
+
// The opbeans pipeline will trigger a release for the master branch
|
|
253
|
+
gitPush()
|
|
254
|
+
// The opbeans pipeline will trigger a release for the release tag
|
|
255
|
+
gitCreateTag(tag: "${env.BRANCH_NAME}")
|
|
256
|
+
}
|
|
257
|
+
}
|
|
239
258
|
}
|
|
240
259
|
}
|
|
241
260
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
261
|
+
}
|
|
262
|
+
post {
|
|
263
|
+
cleanup {
|
|
264
|
+
notifyBuildResult()
|
|
246
265
|
}
|
|
247
266
|
}
|
|
267
|
+
}
|
|
248
268
|
|
|
249
269
|
/**
|
|
250
270
|
Run benchmarks for a Ruby version, then report the results to the Elasticsearch server.
|
|
@@ -256,26 +276,64 @@ def runBenchmark(version){
|
|
|
256
276
|
// - docker.elastic.co/observability-ci/jruby:9.2-12-jdk to jruby-9.2-12-jdk
|
|
257
277
|
// - jruby:9.1 to jruby-9.1
|
|
258
278
|
def transformedVersion = version.replaceAll('.*/', '').replaceAll(':', '-')
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
279
|
+
withEnv(["HOME=${env.WORKSPACE}/${transformedVersion}"]){
|
|
280
|
+
dir("${transformedVersion}"){
|
|
281
|
+
deleteDir()
|
|
282
|
+
unstash 'source'
|
|
283
|
+
dir("${BASE_DIR}"){
|
|
284
|
+
retry(2){
|
|
285
|
+
sleep randomNumber(min:10, max: 30)
|
|
286
|
+
dockerLogin(secret: "${DOCKER_SECRET}", registry: "${DOCKER_REGISTRY}")
|
|
287
|
+
}
|
|
288
|
+
try{
|
|
289
|
+
sh """./spec/scripts/benchmarks.sh "${version}" "${REFERENCE_REPO}" """
|
|
290
|
+
} catch(e){
|
|
291
|
+
throw e
|
|
292
|
+
} finally {
|
|
293
|
+
archiveArtifacts(
|
|
294
|
+
allowEmptyArchive: true,
|
|
295
|
+
artifacts: "**/benchmark-${transformedVersion}.raw,**/benchmark-${transformedVersion}.error",
|
|
296
|
+
onlyIfSuccessful: false)
|
|
297
|
+
sendBenchmarks(file: "benchmark-${transformedVersion}.bulk",
|
|
298
|
+
index: "benchmark-ruby", archive: true)
|
|
299
|
+
}
|
|
267
300
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
class RubyParallelTaskGenerator extends DefaultParallelTaskGenerator {
|
|
308
|
+
|
|
309
|
+
public RubyParallelTaskGenerator(Map params){
|
|
310
|
+
super(params)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
build a clousure that launch and agent and execute the corresponding test script,
|
|
315
|
+
then store the results.
|
|
316
|
+
*/
|
|
317
|
+
public Closure generateStep(x, y){
|
|
318
|
+
return {
|
|
319
|
+
steps.sleep steps.randomNumber(min:10, max: 30)
|
|
320
|
+
steps.node('linux && immutable'){
|
|
321
|
+
// Label is transformed to avoid using the internal docker registry in the x coordinate
|
|
322
|
+
// TODO: def label = "${tag}:${x?.drop(x?.lastIndexOf('/')+1)}#${y}"
|
|
323
|
+
def label = "${tag}:${x}#${y}"
|
|
324
|
+
try {
|
|
325
|
+
steps.runScript(label: label, ruby: x, framework: y)
|
|
326
|
+
saveResult(x, y, 1)
|
|
327
|
+
} catch(e){
|
|
328
|
+
saveResult(x, y, 0)
|
|
329
|
+
steps.error("${label} tests failed : ${e.toString()}\n")
|
|
330
|
+
} finally {
|
|
331
|
+
steps.junit(allowEmptyResults: false,
|
|
332
|
+
keepLongStdio: true,
|
|
333
|
+
testResults: "**/spec/junit-reports/**/ruby-agent-junit.xml")
|
|
334
|
+
steps.dir("${steps.env.BASE_DIR}"){
|
|
335
|
+
steps.archiveArtifacts(artifacts: 'coverage/matrix_results/', defaultExcludes: false)
|
|
336
|
+
steps.stash(name: steps.normalise("coverage-${x}-${y}"), includes: 'coverage/matrix_results/', allowEmpty: true)
|
|
279
337
|
}
|
|
280
338
|
}
|
|
281
339
|
}
|
|
@@ -283,19 +341,79 @@ def runBenchmark(version){
|
|
|
283
341
|
}
|
|
284
342
|
}
|
|
285
343
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
344
|
+
/**
|
|
345
|
+
Run all the tests for the given file with the frameworks to test
|
|
346
|
+
*/
|
|
347
|
+
def runTests(frameworkFile) {
|
|
348
|
+
deleteDir()
|
|
349
|
+
unstash "source"
|
|
350
|
+
dir("${BASE_DIR}"){
|
|
351
|
+
rubyTasksGen = new RubyParallelTaskGenerator(
|
|
352
|
+
xKey: 'RUBY_VERSION',
|
|
353
|
+
yKey: 'FRAMEWORK',
|
|
354
|
+
xFile: ".ci/.jenkins_ruby.yml",
|
|
355
|
+
yFile: frameworkFile,
|
|
356
|
+
exclusionFile: ".ci/.jenkins_exclude.yml",
|
|
357
|
+
tag: "Ruby",
|
|
358
|
+
name: "Ruby",
|
|
359
|
+
steps: this
|
|
294
360
|
)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
361
|
+
def testTasks = rubyTasksGen.generateParallelTests()
|
|
362
|
+
parallel(testTasks)
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
Run tests for a Ruby version and framework version.
|
|
368
|
+
*/
|
|
369
|
+
def runScript(Map params = [:]){
|
|
370
|
+
def label = params.label
|
|
371
|
+
def ruby = params.ruby
|
|
372
|
+
def framework = params.framework
|
|
373
|
+
log(level: 'INFO', text: "${label}")
|
|
374
|
+
retry(2){
|
|
375
|
+
withEnv(["HOME=${env.WORKSPACE}", "PATH=${env.PATH}:${env.WORKSPACE}/bin"]) {
|
|
376
|
+
deleteDir()
|
|
377
|
+
unstash 'source'
|
|
378
|
+
dir("${BASE_DIR}"){
|
|
379
|
+
sleep randomNumber(min:10, max: 30)
|
|
380
|
+
dockerLogin(secret: "${DOCKER_SECRET}", registry: "${DOCKER_REGISTRY}")
|
|
381
|
+
sh("./spec/scripts/spec.sh ${ruby} ${framework}")
|
|
382
|
+
}
|
|
299
383
|
}
|
|
300
384
|
}
|
|
301
385
|
}
|
|
386
|
+
|
|
387
|
+
def convergeCoverage() {
|
|
388
|
+
deleteDir()
|
|
389
|
+
unstash('source')
|
|
390
|
+
dir("${BASE_DIR}"){
|
|
391
|
+
rubyTasksGen.dumpMatrix('-')?.each {
|
|
392
|
+
unstash(normalise("coverage-${it}"))
|
|
393
|
+
}
|
|
394
|
+
sh(script: './spec/scripts/coverage_converge.sh')
|
|
395
|
+
cobertura coberturaReportFile: 'coverage/coverage.xml', onlyStable: false
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
def prepareRelease(Closure body){
|
|
400
|
+
dir("${env.BASE_DIR}"){
|
|
401
|
+
docker.image("${env.RUBY_DOCKER_TAG}").inside('-v ${REFERENCE_REPO}:${REFERENCE_REPO} -v /etc/passwd:/etc/passwd -v ${HOME}/.ssh:${HOME}/.ssh') {
|
|
402
|
+
withEnv(["HOME=${env.WORKSPACE}/${env.BASE_DIR ?: ''}"]) {
|
|
403
|
+
rubygemsLogin.withApi(secret: "${env.RELEASE_SECRET}") {
|
|
404
|
+
withGitRelease(credentialsId: '2a9602aa-ab9f-4e52-baf3-b71ca88469c7-UserAndToken') {
|
|
405
|
+
sh 'gem install rake yard rspec'
|
|
406
|
+
body()
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Transform the versions like:
|
|
415
|
+
// - docker.elastic.co/observability-ci/jruby:9.2-12-jdk to jruby-9.2-12-jdk
|
|
416
|
+
// - jruby:9.1 to jruby-9.1
|
|
417
|
+
def normalise(def what) {
|
|
418
|
+
return what.replaceAll('.*/', '').replaceAll(':', '-')
|
|
419
|
+
}
|