pairing_matrix 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a24d92aeba174106798232ac2570da8ea5ddf5cc
4
+ data.tar.gz: 94d836a0763678f90074e3b3cf831945e691a3ae
5
+ SHA512:
6
+ metadata.gz: 0b599d6687f9536052b4530d9823f39debc2ea8d9504ef537ba28c97928a2474ffea5ae20ec4e61e191583651e3f31e37497ebd2eb36e719986ddff60b1b4f5a
7
+ data.tar.gz: e4be005157901c880b54d1d6544c92fa9753a9503ad823cd1d656a5f914c6987f566e4c5e09064055af5038f3c89146e19f83af8584663be048ea7295131ddca
@@ -0,0 +1,25 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ .idea
24
+ *.iml
25
+ *.yml
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'sinatra'
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2017 Ajit Singh
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,72 @@
1
+ # PairingMatrix
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'pairing_matrix'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install pairing_matrix
18
+
19
+ ## Usage
20
+
21
+ Once you have installed the pairing matrix gem, simply run the pairing_matrix command as shown below and the gem will start the web server. Then hit the url ```localhost:4567/matrix``` in the browser.
22
+
23
+ ```pairing_matrix```
24
+
25
+ To run the web server successfully, it needs a configuration file with name ```pairing_matrix.yml``` in the same directory where you are running the command.
26
+
27
+ ### Here is a sample pairing_matrix.yml file
28
+
29
+ ```yml
30
+ authors_regex: ^.*\[([\w]*)(?:\/)?([\w]*)\].*$
31
+ repos:
32
+ - /Users/Ajit/projects/project1
33
+ - /Users/Ajit/projects/project2
34
+ - /Users/Ajit/projects/project3
35
+ ```
36
+
37
+ authors_regex is the regex which extracts developers name from the commit message. This regex will depend on the commit message format that you follow in your project.
38
+
39
+ ## Contributing
40
+
41
+ 1. Fork it ( https://github.com/ajitsing/pairing_matrix/fork )
42
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
43
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
44
+ 4. Push to the branch (`git push origin my-new-feature`)
45
+ 5. Create a new Pull Request
46
+
47
+
48
+ ## License
49
+ ```LICENSE
50
+ Copyright (c) 2017 Ajit Singh
51
+
52
+ MIT License
53
+
54
+ Permission is hereby granted, free of charge, to any person obtaining
55
+ a copy of this software and associated documentation files (the
56
+ "Software"), to deal in the Software without restriction, including
57
+ without limitation the rights to use, copy, modify, merge, publish,
58
+ distribute, sublicense, and/or sell copies of the Software, and to
59
+ permit persons to whom the Software is furnished to do so, subject to
60
+ the following conditions:
61
+
62
+ The above copyright notice and this permission notice shall be
63
+ included in all copies or substantial portions of the Software.
64
+
65
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
66
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
67
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
68
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
69
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
70
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
71
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
72
+ ```
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/pairing_matrix'
3
+
4
+ run PairingMatrix::Server.run!
@@ -0,0 +1,2 @@
1
+ project_root = File.dirname(File.absolute_path(__FILE__))
2
+ Dir.glob(project_root + '/pairing_matrix/**/*.rb', &method(:require))
@@ -0,0 +1,42 @@
1
+ require 'date'
2
+
3
+ module PairingMatrix
4
+ class CommitReader
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def read(since)
10
+ commits = []
11
+ @config.repos.each do |repo|
12
+ Dir.chdir repo do
13
+ commits << read_commits(since)
14
+ end
15
+ end
16
+ commits.flatten
17
+ end
18
+
19
+ def authors(since)
20
+ commits = read(since)
21
+ commits.map do |commit|
22
+ commit.scan(/#{@config.authors_regex}/).flatten.compact.reject(&:empty?).sort.join(',')
23
+ end.compact.reject(&:empty?)
24
+ end
25
+
26
+ def authors_with_commits(days)
27
+ date = (Date.today - days).to_s
28
+ authors = authors(date)
29
+ author_groups = authors.group_by { |n| n }
30
+ author_groups.map do |k, v|
31
+ pair = k.split(',')
32
+ pair.unshift('') if pair.size == 1
33
+ [pair, v.size].flatten
34
+ end
35
+ end
36
+
37
+ private
38
+ def read_commits(since)
39
+ `git log --oneline --after=\"#{since}\"`.split("\n")
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,10 @@
1
+ module PairingMatrix
2
+ class Config
3
+ attr_reader :repos, :authors_regex
4
+
5
+ def initialize(repos, authors_regex)
6
+ @repos = repos
7
+ @authors_regex = authors_regex
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ require 'YAML'
2
+
3
+ module PairingMatrix
4
+ class ConfigReader
5
+ def initialize(config_file)
6
+ @config_file = config_file
7
+ end
8
+
9
+ def config
10
+ raw_config = YAML::load_file @config_file
11
+ PairingMatrix::Config.new(raw_config['repos'], raw_config['authors_regex'])
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>Pairing Matrix</title>
5
+ <link rel="stylesheet" href="style.css"/>
6
+
7
+ <script language="javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.js"></script>
8
+ <script language="javascript" src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
9
+ <script language="javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.7.3/d3.min.js"></script>
10
+ <script language="javascript" src="matrix.js"></script>
11
+ <script language="javascript" src="index.js"></script>
12
+ </head>
13
+
14
+ <body>
15
+ <div>
16
+ <span>Display data of last</span>
17
+ <input type="number" id="days" value="10"/>
18
+ <span>days</span>
19
+ <button id="visualize">Visualize</button>
20
+ </div>
21
+ <div class="viz_container">
22
+ <svg>
23
+ <g class="area">
24
+ </g>
25
+ </svg>
26
+ </div>
27
+ </body>
28
+ </html>
@@ -0,0 +1,20 @@
1
+ $(document).ready(function () {
2
+ var playground = new PlayGround(".area");
3
+ var renderMatrix = function (days) {
4
+ $.get('/data/' + days, function (data, status) {
5
+ if (status == 'success') {
6
+ playground.load(JSON.parse(data));
7
+
8
+ } else {
9
+ console.log(data);
10
+ alert('error occurred!')
11
+ }
12
+ })
13
+ };
14
+
15
+ $('#visualize').on('click', function () {
16
+ renderMatrix($('#days').val())
17
+ });
18
+
19
+ renderMatrix(10);
20
+ });
@@ -0,0 +1,212 @@
1
+ var playground;
2
+ function PlayGround(selector) {
3
+ this.el = null;
4
+ this.centerX = 400;
5
+ this.centerY = 350;
6
+ this.radius = 300;
7
+ this.selector = selector;
8
+
9
+ this.pairingData = [];
10
+ this.playersData = [];
11
+
12
+ this.connectionScale = d3.scaleLinear()
13
+ .domain([0, 200])
14
+ .range([0, 50]);
15
+
16
+ this.init = function () {
17
+ playground = this;
18
+ this.el = d3.select(this.selector);
19
+ };
20
+
21
+ this.load = function (pairingData) {
22
+ this.pairingData = pairingData;
23
+ this.validPairsData = this.getValidPairs(pairingData);
24
+ this.playersData = this.getPlayerNames(pairingData);
25
+ this.clear();
26
+ this.setGround();
27
+ this.setPairing();
28
+ this.setPlayers();
29
+ };
30
+
31
+ this.clear = function () {
32
+ $(this.selector).children().remove();
33
+ }
34
+
35
+ this.setPairing = function () {
36
+ this.el.selectAll(".connect")
37
+ .data(playground.validPairsData)
38
+ .enter().append("path")
39
+ .attr("class", "connect")
40
+ .attr("d", function (d) {
41
+ var fromIndex = _.indexOf(playground.playersData, d[0]),
42
+ fromCoordinates = playground.getPlayerCoordinates(fromIndex);
43
+ var toIndex = (d[1] == "") ? fromIndex : _.indexOf(playground.playersData, d[1]),
44
+ toCoordinates = playground.getPlayerCoordinates(toIndex);
45
+ return "M " + fromCoordinates.x + " " + fromCoordinates.y + " Q 400 350 " +
46
+ toCoordinates.x + " " + toCoordinates.y;
47
+ })
48
+ .attr("fill", "none")
49
+ .attr("stroke", "#DD1031")
50
+ .attr("stroke-width", function (d) {
51
+ return playground.connectionScale(d[2])
52
+ })
53
+ .attr("stroke-opacity", 0.75)
54
+ .attr("data-from", function (d) {
55
+ return d[0]
56
+ })
57
+ .attr("data-to", function (d) {
58
+ return d[1]
59
+ });
60
+ };
61
+
62
+ this.setGround = function () {
63
+ this.el.append("circle")
64
+ .attr("cx", this.centerX)
65
+ .attr("cy", this.centerY)
66
+ .attr("r", this.radius)
67
+ .style("fill-opacity", 0.05);
68
+ };
69
+
70
+ this.setPlayers = function () {
71
+ var colors = d3.scaleOrdinal(d3.schemeCategory20);
72
+ var players = this.el.selectAll(".players")
73
+ .data(playground.playersData)
74
+ .enter()
75
+ .append("g");
76
+ players.append("circle")
77
+ .attr("cx", function (d, i) {
78
+ return playground.getPlayerCoordinates(i).x
79
+ })
80
+ .attr("cy", function (d, i) {
81
+ return playground.getPlayerCoordinates(i).y
82
+ })
83
+ .attr("r", 20)
84
+ .attr("fill", colors)
85
+ .attr("fill-opacity", 0.75)
86
+ .attr("stroke", "#EE1031")
87
+ .attr("stroke-opacity", 0.75)
88
+ .attr("stroke-width", function (d) {
89
+ return playground.connectionScale(playground.getSoloContribution(d))
90
+ })
91
+ .attr("z-index", 10)
92
+ .attr("class", "player")
93
+ .attr("id", function (d) {
94
+ return d
95
+ })
96
+ .call(this.dragger());
97
+ players.append("text")
98
+ .attr("class", "player_names")
99
+ .text(function (d) {
100
+ return d
101
+ })
102
+ .attr("x", function (d, i) {
103
+ return playground.getPlayerCoordinates(i).x + 2
104
+ })
105
+ .attr("y", function (d, i) {
106
+ return playground.getPlayerCoordinates(i).y + 3
107
+ })
108
+ .attr("fill", "#000000");
109
+ };
110
+
111
+ this.dragger = function () {
112
+ return d3.drag().on("drag", function (d) {
113
+ var player = d3.select(this);
114
+ var playerName = $(player[0]).siblings(".player_names");
115
+ var newPoint = playground.closestPointOnCircumference();
116
+ player.attr("cx", newPoint[0]);
117
+ player.attr("cy", newPoint[1]);
118
+ playerName.attr("x", function () {
119
+ return newPoint[0] + 2
120
+ });
121
+ playerName.attr("y", function () {
122
+ return newPoint[1] + 3
123
+ });
124
+ playground.updateConnectorsPath(player.attr("id"), newPoint);
125
+ })
126
+ };
127
+
128
+ this.updateConnectorsPath = function (playerId, newPoint) {
129
+ playground.el
130
+ .selectAll(".connect[data-from='" + playerId + "']")
131
+ .attr("d", function (d) {
132
+ return playground.replaceFromInPath($(this).attr("d"), newPoint);
133
+ }
134
+ );
135
+ playground.el
136
+ .selectAll(".connect[data-to='" + playerId + "']")
137
+ .attr("d", function (d) {
138
+ return playground.replaceToInPath($(this).attr("d"), newPoint);
139
+ }
140
+ );
141
+ }
142
+
143
+ this.getCollidingPlayer = function (player) {
144
+ var svg = $("svg")[0];
145
+ var rectangle = svg.createSVGRect();
146
+ rectangle.x = player.attr("cx");
147
+ rectangle.y = player.attr("cy");
148
+ rectangle.width = player.attr("r");
149
+ rectangle.height = player.attr("r");
150
+ var allElements = svg.getIntersectionList(rectangle, null);
151
+ return _.filter(allElements, function (elem) {
152
+ return $(elem).attr("class") == "player" && $(elem).attr("id") != player.attr("id")
153
+ });
154
+ }
155
+
156
+ this.replaceFromInPath = function (path, from) {
157
+ var parts = path.split(" ");
158
+ parts[1] = from[0];
159
+ parts[2] = from[1];
160
+ return parts.join(" ");
161
+ }
162
+
163
+ this.replaceToInPath = function (path, to) {
164
+ var parts = path.split(" ");
165
+ parts[6] = to[0];
166
+ parts[7] = to[1];
167
+ return parts.join(" ");
168
+
169
+ }
170
+
171
+ this.closestPointOnCircumference = function () {
172
+ var vx = d3.event.x - this.centerX,
173
+ vy = d3.event.y - this.centerY,
174
+ magV = Math.sqrt(vx * vx + vy * vy);
175
+ return [(this.centerX + vx / magV * this.radius), (this.centerY + vy / magV * this.radius)];
176
+ };
177
+
178
+ this.getPlayerCoordinates = function (index) {
179
+ var distanceInDegrees = 2 * Math.PI / playground.playersData.length;
180
+ return {
181
+ x: (playground.radius * Math.sin(distanceInDegrees * index) + playground.centerX),
182
+ y: (playground.radius * Math.cos(distanceInDegrees * index) + playground.centerY)
183
+ }
184
+ };
185
+
186
+ this.getValidPairs = function (pairingData) {
187
+ return _.filter(pairingData, function (data) {
188
+ return (!_.isEmpty(data[0]) && !_.isEmpty(data[1]));
189
+ })
190
+ };
191
+
192
+ this.getPlayerNames = function (pairingData) {
193
+ var playerNames = [];
194
+ _.each(pairingData, function (data) {
195
+ playerNames.push(data[0]);
196
+ playerNames.push(data[1]);
197
+ });
198
+ playerNames = _.compact(playerNames);
199
+ return _.sortBy(_.uniq(playerNames), function (d) {
200
+ return d
201
+ });
202
+ };
203
+
204
+ this.getSoloContribution = function (name) {
205
+ var contrib = _.find(this.pairingData, function (data) {
206
+ return ((data[0] == name && _.isEmpty(data[1])) || (data[1] == name && _.isEmpty(data[0])));
207
+ });
208
+ return contrib ? contrib[2] : 1;
209
+ };
210
+
211
+ this.init();
212
+ }
@@ -0,0 +1,65 @@
1
+ .row-fluid [class*="span"] {
2
+ margin-left: 1%;
3
+ }
4
+
5
+ body {
6
+ padding: 10px;
7
+ }
8
+
9
+ .navbar {
10
+ position: absolute;
11
+ top: 5px;
12
+ right: 20px;
13
+ }
14
+
15
+ textarea {
16
+ width: 100%;
17
+ height: 5000px;
18
+ resize: vertical;
19
+ box-sizing: border-box;
20
+ overflow-y: hidden;
21
+ }
22
+
23
+ textarea#pairing_text {
24
+ width: 2000px;
25
+ }
26
+
27
+ .input-log {
28
+ overflow-x: hidden;
29
+ }
30
+
31
+ #regex {
32
+ width: 100%;
33
+ box-sizing: border-box;
34
+ padding: 20px;
35
+ margin-top: 10px;
36
+ }
37
+
38
+ #go {
39
+ width: 100%;
40
+ box-sizing: border-box;
41
+ padding: 9px;
42
+ margin-top: 10px;
43
+ }
44
+
45
+ .viz_container {
46
+ text-align: center;
47
+ }
48
+
49
+ svg {
50
+ height: 700px;
51
+ width: 815px;
52
+ }
53
+
54
+ .player {
55
+ cursor: pointer;
56
+ }
57
+
58
+ .player_names {
59
+ pointer-events: none;
60
+ }
61
+
62
+ .input-container {
63
+ height: 300px;
64
+ overflow: scroll;
65
+ }
@@ -0,0 +1,19 @@
1
+ require 'sinatra/base'
2
+ require 'json'
3
+ require_relative '../../pairing_matrix'
4
+
5
+ module PairingMatrix
6
+ class Server < Sinatra::Base
7
+ config_reader = PairingMatrix::ConfigReader.new('pairing_matrix.yml')
8
+ config = config_reader.config
9
+ commit_reader = PairingMatrix::CommitReader.new(config)
10
+
11
+ get '/data/:days' do
12
+ commit_reader.authors_with_commits(params['days'].to_i).to_json
13
+ end
14
+
15
+ get '/matrix' do
16
+ File.read(File.join(File.dirname(__FILE__), 'public/index.html'))
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module PairingMatrix
2
+ VERSION = '0.1'
3
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pairing_matrix/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'pairing_matrix'
8
+ spec.version = PairingMatrix::VERSION
9
+ spec.authors = ['Ajit Singh']
10
+ spec.email = ['jeetsingh.ajit@gmail.com']
11
+ spec.summary = 'Draw pairing matrix from given repos and configurations'
12
+ spec.description = 'Draw pairing matrix from given repos and configurations'
13
+ spec.homepage = 'https://github.com/ajitsing/pairing_matrix'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pairing_matrix
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Ajit Singh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-03-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ description: Draw pairing matrix from given repos and configurations
28
+ email:
29
+ - jeetsingh.ajit@gmail.com
30
+ executables:
31
+ - pairing_matrix
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".gitignore"
36
+ - Gemfile
37
+ - LICENSE.txt
38
+ - README.md
39
+ - Rakefile
40
+ - bin/pairing_matrix
41
+ - lib/pairing_matrix.rb
42
+ - lib/pairing_matrix/commit_reader.rb
43
+ - lib/pairing_matrix/config/config.rb
44
+ - lib/pairing_matrix/config/config_reader.rb
45
+ - lib/pairing_matrix/server/public/index.html
46
+ - lib/pairing_matrix/server/public/index.js
47
+ - lib/pairing_matrix/server/public/matrix.js
48
+ - lib/pairing_matrix/server/public/style.css
49
+ - lib/pairing_matrix/server/server.rb
50
+ - lib/pairing_matrix/version.rb
51
+ - pairing_matrix.gemspec
52
+ homepage: https://github.com/ajitsing/pairing_matrix
53
+ licenses:
54
+ - MIT
55
+ metadata: {}
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubyforge_project:
72
+ rubygems_version: 2.5.1
73
+ signing_key:
74
+ specification_version: 4
75
+ summary: Draw pairing matrix from given repos and configurations
76
+ test_files: []