decidim-maintainers_toolbox 0.1.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.
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/time/zones"
4
+ require_relative "base"
5
+
6
+ module Decidim
7
+ module MaintainersToolbox
8
+ module GithubManager
9
+ module Querier
10
+ # Makes a GET request for the list of Issues or Pull Requests in GitHub.
11
+ # They must comply the following conditions:
12
+ # * To be merged in the period between the days to check from and today. (90 days by default)
13
+ # * To have the label that we are querying ("type: fix" by default)
14
+ # * To not have any of the excluded labels (["backport", "no-backport"] by default)
15
+ # * To have been merged
16
+ #
17
+ # @param token [String] token for GitHub authentication
18
+ # @param days_to_check_from [Integer] the number of days since the pull requests were merged from when we will start the check
19
+ # @param label [String] the label that we want to search by
20
+ # @param exclude_labels [Array] the labels that we want to exclude in the search
21
+ #
22
+ # @see https://docs.github.com/en/rest/issues/issues#list-repository-issues GitHub API documentation
23
+ class ByLabel < Decidim::MaintainersToolbox::GithubManager::Querier::Base
24
+ def initialize(token:, days_to_check_from: 90, label: "type: fix", exclude_labels: ["backport", "no-backport"])
25
+ @label = label
26
+ @exclude_labels = exclude_labels
27
+ @token = token
28
+ @days_to_check_from = days_to_check_from
29
+ end
30
+
31
+ # Makes the GET request and parses the response of an Issue or Pull Request in GitHub
32
+ #
33
+ # @return [Hash]
34
+ def call
35
+ parse json_response("https://api.github.com/repos/decidim/decidim/issues")
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :label, :exclude_labels, :days_to_check_from
41
+
42
+ def headers
43
+ Time.zone = "UTC"
44
+
45
+ {
46
+ labels: label,
47
+ state: "closed",
48
+ per_page: 100,
49
+ since: since.iso8601
50
+ }
51
+ end
52
+
53
+ def since
54
+ Time.zone.today - days_to_check_from
55
+ end
56
+
57
+ # Parses the response of an Issue or Pull Request in GitHub
58
+ #
59
+ # @return [Hash]
60
+ def parse(metadata)
61
+ metadata.map do |item|
62
+ next if has_any_of_excluded_labels?(item)
63
+ next unless merged?(item)
64
+ next unless merged_in_date_range?(item)
65
+
66
+ {
67
+ id: item["number"],
68
+ title: item["title"]
69
+ }
70
+ end.compact
71
+ end
72
+
73
+ def has_any_of_excluded_labels?(item)
74
+ item["labels"].map { |label| label.map { |_key, val| exclude_labels.include?(val) } }.flatten.any? true
75
+ end
76
+
77
+ def merged_at(item)
78
+ Date.parse(item["pull_request"]["merged_at"])
79
+ end
80
+
81
+ def merged?(item)
82
+ return false if item["pull_request"].nil?
83
+ return false if item["pull_request"]["merged_at"].nil?
84
+
85
+ merged_at(item).present?
86
+ rescue TypeError, Date::Error
87
+ false
88
+ end
89
+
90
+ def merged_in_date_range?(item)
91
+ merged_at(item) > since
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Decidim
6
+ module MaintainersToolbox
7
+ module GithubManager
8
+ module Querier
9
+ # Makes a GET request for the list of Issues or Pull Requests in GitHub.
10
+ #
11
+ # @param token [String] token for GitHub authentication
12
+ # @param title [String] the title that we want to search by
13
+ # @param state [String] the state of the issue. By default is "open"
14
+ #
15
+ # @see https://docs.github.com/en/rest/issues/issues#list-repository-issues GitHub API documentation
16
+ class ByTitle < Decidim::MaintainersToolbox::GithubManager::Querier::Base
17
+ def initialize(title:, token:, state: "open")
18
+ @title = title
19
+ @token = token
20
+ @state = state
21
+ end
22
+
23
+ # Makes the GET request and parses the response of an Issue or Pull Request in GitHub
24
+ #
25
+ # @return [Hash]
26
+ def call
27
+ data = json_response("https://api.github.com/repos/decidim/decidim/issues")
28
+
29
+ parse(data)
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :title, :state
35
+
36
+ def headers
37
+ {
38
+ title: title,
39
+ state: state,
40
+ per_page: 100
41
+ }
42
+ end
43
+
44
+ # Parses the response of an Issue or Pull Request in GitHub
45
+ #
46
+ # @return [Hash]
47
+ def parse(metadata)
48
+ metadata.map do |item|
49
+ {
50
+ id: item["number"],
51
+ title: item["title"]
52
+ }
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Decidim
6
+ module MaintainersToolbox
7
+ module GithubManager
8
+ module Querier
9
+ # Makes a GET request for the related issues of an Issue or Pull Request in GitHub
10
+ # Uses the Timeline events API endpoint
11
+ #
12
+ # @see https://docs.github.com/en/rest/issues/timeline?apiVersion=2022-11-28 GitHub API documentation
13
+ class RelatedIssues < Decidim::MaintainersToolbox::GithubManager::Querier::Base
14
+ def initialize(issue_id:, token:)
15
+ @issue_id = issue_id
16
+ @token = token
17
+ end
18
+
19
+ # Makes the GET request and parses the response of an Issue or Pull Request in GitHub
20
+ #
21
+ # @return [Hash]
22
+ def call
23
+ parse(json_response("https://api.github.com/repos/decidim/decidim/issues/#{@issue_id}/timeline"))
24
+ end
25
+
26
+ private
27
+
28
+ def headers
29
+ { per_page: 100 }
30
+ end
31
+
32
+ # Parses the response of an Issue or Pull Request in GitHub
33
+ #
34
+ # @return [Hash]
35
+ def parse(metadata)
36
+ references = metadata.select do |item|
37
+ item["event"] == "cross-referenced" && item["source"]["issue"]["repository"]["full_name"] == "decidim/decidim"
38
+ end
39
+ references.map do |item|
40
+ issue = item["source"]["issue"]
41
+
42
+ {
43
+ id: issue["number"],
44
+ title: issue["title"].strip,
45
+ state: issue.dig("pull_request", "merged_at").nil? ? issue["state"] : "merged"
46
+ }
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "json"
5
+
6
+ require_relative "querier/by_issue_id"
7
+ require_relative "querier/by_label"
8
+ require_relative "querier/related_issues"
9
+
10
+ module Decidim
11
+ module MaintainersToolbox
12
+ module GithubManager
13
+ # Allows to make GET requests to GitHub Rest API about Issues and Pull Requests
14
+ # @see https://docs.github.com/en/rest
15
+ module Querier
16
+ autoload :ByIssueId, "decidim/maintainers_toolbox/github_manager/querier/by_issue_id"
17
+ autoload :ByLabel, "decidim/maintainers_toolbox/github_manager/querier/by_label"
18
+ autoload :ByTitle, "decidim/maintainers_toolbox/github_manager/querier/by_title"
19
+ autoload :RelatedIssues, "decidim/maintainers_toolbox/github_manager/querier/related_issues"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,287 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+ require_relative "github_manager/poster"
5
+ require_relative "github_manager/querier/by_title"
6
+ require_relative "changelog_generator"
7
+
8
+ module Decidim
9
+ module MaintainersToolbox
10
+ class Releaser
11
+ class InvalidMetadataError < StandardError; end
12
+
13
+ class InvalidBranchError < StandardError; end
14
+
15
+ class InvalidVersionTypeError < StandardError; end
16
+
17
+ DECIDIM_VERSION_FILE = ".decidim-version"
18
+
19
+ # @param token [String] token for GitHub authentication
20
+ # @param version_type [String] The kind of release that you want to prepare. Supported values: rc, minor, patch
21
+ # @param working_dir [String] current working directory. Useful for testing purposes
22
+ # @param exit_with_unstaged_changes [Boolean] wheter we should exit cowardly if there is any unstaged change
23
+ def initialize(token:, version_type:, working_dir: Dir.pwd, exit_with_unstaged_changes: false)
24
+ @token = token
25
+ @version_type = version_type
26
+ @working_dir = working_dir
27
+ @exit_with_unstaged_changes = exit_with_unstaged_changes
28
+ end
29
+
30
+ def call
31
+ Dir.chdir(@working_dir) do
32
+ exit_if_unstaged_changes if @exit_with_unstaged_changes
33
+ exit_if_pending_crowdin_pull_request
34
+
35
+ puts "Starting the release process for #{version_number} in 10 seconds"
36
+ sleep 10
37
+
38
+ run("git checkout #{release_branch}")
39
+ run("git pull origin #{release_branch}")
40
+ bump_decidim_version
41
+ run("bin/rake update_versions")
42
+ run("bin/rake patch_generators")
43
+ run("bin/rake bundle")
44
+ run("npm install")
45
+
46
+ check_tests
47
+
48
+ generate_changelog
49
+
50
+ run("git checkout -b chore/prepare/#{version_number}")
51
+ run("git commit -a -m 'Prepare #{version_number} release'")
52
+ run("git push origin chore/prepare/#{version_number}")
53
+
54
+ create_pull_request
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ # The git branch
61
+ #
62
+ # @return [String]
63
+ def branch
64
+ @branch ||= capture("git rev-parse --abbrev-ref HEAD")[0].strip
65
+ end
66
+
67
+ # Raise an error if the branch does not start with the preffix "release/"
68
+ # or returns the branch name
69
+ #
70
+ # @raise [InvalidBranchError]
71
+ #
72
+ # @return [String]
73
+ def release_branch
74
+ raise InvalidBranchError, "This is not a release branch, aborting" unless branch.start_with?("release/")
75
+
76
+ branch
77
+ end
78
+
79
+ # Changes the decidim version in the file
80
+ #
81
+ # @return [void]
82
+ def bump_decidim_version
83
+ File.write(DECIDIM_VERSION_FILE, version_number)
84
+ end
85
+
86
+ # The version number for the release that we are preparing
87
+ #
88
+ # @todo support the "minor" type version
89
+ #
90
+ # @return [String] the version number
91
+ def version_number
92
+ @version_number ||= case @version_type
93
+ when "rc"
94
+ next_version_number_for_release_candidate(old_version_number)
95
+ when "patch"
96
+ next_version_number_for_patch_release(old_version_number)
97
+ else
98
+ raise InvalidVersionTypeError, "This is not a supported version type"
99
+ end
100
+ end
101
+
102
+ def parsed_version_number(version_number)
103
+ /(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)/ =~ version_number
104
+
105
+ [major.to_i, minor.to_i, patch.to_i]
106
+ end
107
+
108
+ # Given a version number, returns the next release candidate
109
+ #
110
+ # If the current version number is `dev`, then we return the `rc1` version
111
+ # If the current version number is `rc`, then we return the next `rc` version
112
+ # Else, it means is a `minor` or `patch` version. On those cases we raise an Exception, as releases candidates should
113
+ # be only done from a `dev` or a `rc` version.
114
+ #
115
+ # @raise [InvalidVersionTypeError]
116
+ #
117
+ # @param current_version_number [String] - The version number of the current version
118
+ #
119
+ # @return [String] - the new version number
120
+ def next_version_number_for_release_candidate(current_version_number)
121
+ if current_version_number.include? "dev"
122
+ major, minor, patch = parsed_version_number(current_version_number)
123
+ new_version_number = "#{major}.#{minor}.#{patch}.rc1"
124
+ elsif current_version_number.include? "rc"
125
+ new_rc_number = current_version_number.match(/rc(\d)/)[1].to_i + 1
126
+ new_version_number = current_version_number.gsub(/rc\d/, "rc#{new_rc_number}")
127
+ else
128
+ error_message = <<-EOMESSAGE
129
+ Trying to do a release candidate version from patch release. Bailing out.
130
+ You need to do a release candidate from a `dev` or from another `rc` version
131
+ EOMESSAGE
132
+ raise InvalidVersionTypeError, error_message
133
+ end
134
+
135
+ new_version_number
136
+ end
137
+
138
+ # Given a version number, returns the next patch release
139
+ #
140
+ # If the current version number is `dev`, then we raise an Exception, as you need to first do a release candidate.
141
+ # If the current version number is `rc`, then we return the `0` patch version
142
+ # Else, it means is a `patch` version, so we return the next patch version
143
+ #
144
+ # @raise [InvalidVersionTypeError]
145
+ #
146
+ # @param current_version_number [String] - The version number of the current version
147
+ #
148
+ # @return [String] - the new version number
149
+ def next_version_number_for_patch_release(current_version_number)
150
+ major, minor, patch = parsed_version_number(current_version_number)
151
+
152
+ if current_version_number.include? "dev"
153
+ error_message = <<-EOMESSAGE
154
+ Trying to do a patch version from dev release. Bailing out.
155
+ You need to do first a release candidate.
156
+ EOMESSAGE
157
+ raise InvalidVersionTypeError, error_message
158
+ elsif current_version_number.include? "rc"
159
+ new_version_number = "#{major}.#{minor}.0"
160
+ else
161
+ new_version_number = "#{major}.#{minor}.#{patch.to_i + 1}"
162
+ end
163
+
164
+ new_version_number
165
+ end
166
+
167
+ # The version number from the file
168
+ #
169
+ # @return [String] the version number
170
+ def old_version_number
171
+ File.read(DECIDIM_VERSION_FILE).strip
172
+ end
173
+
174
+ # Run the tests and if fails restore the changes using git and exit with an error
175
+ #
176
+ # @return [void]
177
+ def check_tests
178
+ puts "Running specs"
179
+ output, status = capture("bin/rspec")
180
+
181
+ unless status.success?
182
+ run("git restore .")
183
+ puts output
184
+ exit_with_errors("Tests execution failed. Fix the errors and run again.")
185
+ end
186
+ end
187
+
188
+ # Generates the changelog taking into account the last time the version changed
189
+ #
190
+ # @return [void]
191
+ def generate_changelog
192
+ sha_version = capture("git log -n 1 --pretty=format:%h -- .decidim-version")[0]
193
+ ChangeLogGenerator.new(token: @token, since_sha: sha_version).call
194
+ temporary_changelog = File.read("./temporary_changelog.md")
195
+ legacy_changelog = File.read("./CHANGELOG.md")
196
+ version_changelog = "## [#{version_number}](https://github.com/decidim/decidim/tree/#{version_number})\n\n#{temporary_changelog}\n"
197
+ changelog = legacy_changelog.gsub("# Changelog\n\n", "# Changelog\n\n#{version_changelog}")
198
+ File.write("./CHANGELOG.md", changelog)
199
+ end
200
+
201
+ # Creates the pull request for bumping the version
202
+ #
203
+ # @return [void]
204
+ def create_pull_request
205
+ base_branch = release_branch
206
+ head_branch = "chore/prepare/#{version_number}"
207
+
208
+ params = {
209
+ title: "Bump to v#{version_number} version",
210
+ body: "#### :tophat: What? Why?
211
+
212
+ This PR changes the version of the #{release_branch} branch, so we can publish the release once this is approved and merged.
213
+
214
+ #### Testing
215
+
216
+ All the tests should pass, except for some generators tests, that will fail because the gems and NPM packages have not
217
+ been actually published yet (as in sent to rubygems/npm).
218
+ You will see errors such as `No matching version found for @decidim/browserslist-config@~0.xx.y` in the CI logs.
219
+
220
+ :hearts: Thank you!
221
+ ",
222
+ labels: ["type: internal"],
223
+ head: head_branch,
224
+ base: base_branch
225
+ }
226
+ Decidim::MaintainersToolbox::GithubManager::Poster.new(token: @token, params: params).call
227
+ end
228
+
229
+ # Captures to output of a command
230
+ #
231
+ # @return [Array<String, Process::Status>] The stdout and stderr of the command and its status (aka error code)
232
+ def capture(cmd, env: {})
233
+ Open3.capture2e(env, cmd)
234
+ end
235
+
236
+ # Runs a command
237
+ #
238
+ # @return [void]
239
+ def run(cmd, out: $stdout)
240
+ system(cmd, out: out)
241
+ end
242
+
243
+ # Check if there is any open pull request from Crowdin in GitHub
244
+ #
245
+ # @return [Boolean] - true if there is any open PR
246
+ def pending_crowdin_pull_requests?
247
+ pull_requests = Decidim::MaintainersToolbox::GithubManager::Querier::ByTitle.new(token: @token, title: "New Crowdin updates").call
248
+ pull_requests.any?
249
+ end
250
+
251
+ # Exit the script execution if there are any pull request from Crowdin open
252
+ #
253
+ # @return [void]
254
+ def exit_if_pending_crowdin_pull_request
255
+ return unless pending_crowdin_pull_requests?
256
+
257
+ error_message = <<-EOERROR
258
+ There are open pull requests from Crowdin in GitHub
259
+ Merge them and run again this script.
260
+ EOERROR
261
+ exit_with_errors(error_message)
262
+ end
263
+
264
+ # Exit the script execution with a message
265
+ # Exit the script execution if there are any unstaged changes
266
+ #
267
+ # @return [void]
268
+ def exit_if_unstaged_changes
269
+ return if `git diff`.empty?
270
+
271
+ error_message = <<-EOERROR
272
+ There are changes not staged in your project.
273
+ Please commit your changes or stash them.
274
+ EOERROR
275
+ exit_with_errors(error_message)
276
+ end
277
+
278
+ # Exit the script execution with a message
279
+ #
280
+ # @return [void]
281
+ def exit_with_errors(message)
282
+ puts message
283
+ exit 1
284
+ end
285
+ end
286
+ end
287
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module MaintainersToolbox
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "maintainers_toolbox/version"
4
+
5
+ module Decidim
6
+ module MaintainersToolbox
7
+ class Error < StandardError; end
8
+ # Your code goes here...
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: decidim-maintainers_toolbox
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrés Pereira de Lucena
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-03-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ruby-progressbar
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 6.1.7
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 6.1.7
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.12'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.12'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.18'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.18'
97
+ description: Tools for releasing, backporting, changelog generating, and working with
98
+ GitHub
99
+ email:
100
+ - andreslucena@gmail.com
101
+ executables:
102
+ - decidim-backporter
103
+ - decidim-backports-checker
104
+ - decidim-changelog-generator
105
+ - decidim-releaser
106
+ extensions: []
107
+ extra_rdoc_files: []
108
+ files:
109
+ - README.md
110
+ - decidim-maintainers_toolbox.gemspec
111
+ - exe/decidim-backporter
112
+ - exe/decidim-backports-checker
113
+ - exe/decidim-changelog-generator
114
+ - exe/decidim-releaser
115
+ - lib/decidim/maintainers_toolbox.rb
116
+ - lib/decidim/maintainers_toolbox/backporter.rb
117
+ - lib/decidim/maintainers_toolbox/backports_reporter/cli_report.rb
118
+ - lib/decidim/maintainers_toolbox/backports_reporter/csv_report.rb
119
+ - lib/decidim/maintainers_toolbox/backports_reporter/report.rb
120
+ - lib/decidim/maintainers_toolbox/changelog_generator.rb
121
+ - lib/decidim/maintainers_toolbox/git_backport_checker.rb
122
+ - lib/decidim/maintainers_toolbox/git_backport_manager.rb
123
+ - lib/decidim/maintainers_toolbox/github_manager/poster.rb
124
+ - lib/decidim/maintainers_toolbox/github_manager/querier.rb
125
+ - lib/decidim/maintainers_toolbox/github_manager/querier/base.rb
126
+ - lib/decidim/maintainers_toolbox/github_manager/querier/by_issue_id.rb
127
+ - lib/decidim/maintainers_toolbox/github_manager/querier/by_label.rb
128
+ - lib/decidim/maintainers_toolbox/github_manager/querier/by_title.rb
129
+ - lib/decidim/maintainers_toolbox/github_manager/querier/related_issues.rb
130
+ - lib/decidim/maintainers_toolbox/releaser.rb
131
+ - lib/decidim/maintainers_toolbox/version.rb
132
+ homepage: https://decidim.org
133
+ licenses:
134
+ - AGPL-3.0
135
+ metadata:
136
+ bug_tracker_uri: https://github.com/decidim/decidim/issues
137
+ documentation_uri: https://docs.decidim.org/
138
+ funding_uri: https://opencollective.com/decidim
139
+ homepage_uri: https://decidim.org
140
+ source_code_uri: https://github.com/decidim/decidim
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: 2.7.5
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubygems_version: 3.1.6
157
+ signing_key:
158
+ specification_version: 4
159
+ summary: Release related tools for the Decidim project
160
+ test_files: []