captive-release 0.11.0 → 0.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 90b817eaab2a7cab41857f499ee8cac77d460bbf073f9074016b96621a5df027
4
- data.tar.gz: b66c7485162d298b516b2dfb8e6e90bc7ae13d2a88fa59ca12254dacbadea8a4
3
+ metadata.gz: 6a8fd7809824a87b08805de31f9ddc40af157b6ba41e0b76e19f90edd4c9ade2
4
+ data.tar.gz: 33dff63edd19f1889f5fa014271eff8640b6cba8effc7937ab9332fe2bf14316
5
5
  SHA512:
6
- metadata.gz: 9c75bc8285c685ef3c2b2969de5259e1078ccb45f76849f503177c1891352ab38b9705125c4a9787681c931540d7fe3187dc92f1c0184a71e1876ee380659675
7
- data.tar.gz: 4fb8ee0668d2b2848817ba8a4eccaf45f38dec0dd216eaf410e08ef7373edfcef20d7cd7c76563bbdef2629240ec0e1b9e54a18962bd2383307b0eb3551b100a
6
+ metadata.gz: 9cfbc9751d8b2607e05512a7120f3773d3553526786e57d47a45d557e97bda793e26f09be493f015c3caaf982329f5363576a61e14399ecef1fa4157d913b352
7
+ data.tar.gz: 6a114be8ba3248b560619b8e345e1ec8e9a9d9b009b73d7723be59ba743bae25ba519f1f9baf533e772e16608741d0a77fdc1ab42007a359a6a38b49b266ffee
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CaptiveRelease
4
+ # Complète les notes générées par GitHub, qui ne listent que les PR mergées : un commit
5
+ # poussé directement sur la branche n'y apparaît jamais, même quand il porte la feature
6
+ # qui justifie le bump.
7
+ class DirectCommits
8
+ COMMIT_VERSION = /\A🔖 Set version /
9
+
10
+ def initialize(api)
11
+ @api = api
12
+ end
13
+
14
+ # Sans plage, il n'y a pas de tag précédent : lister les commits reviendrait à lister
15
+ # l'historique entier.
16
+ def completer(notes)
17
+ plage = notes.plage or return notes
18
+ lignes = commits(*plage).map { ligne(_1) }
19
+ lignes.empty? ? notes : notes.ajouter("## Commits directs\n#{lignes.join("\n")}\n")
20
+ end
21
+
22
+ private
23
+
24
+ def commits(depuis, jusqu_a)
25
+ @api.get("compare/#{depuis}...#{jusqu_a}")["commits"].reject do |commit|
26
+ titre(commit).match?(COMMIT_VERSION) || !@api.get("commits/#{commit["sha"]}/pulls").empty?
27
+ end
28
+ end
29
+
30
+ def ligne(commit)
31
+ auteur = commit.dig("author", "login")
32
+ "* #{titre(commit)}#{" by @#{auteur}" if auteur} in #{commit["sha"]}"
33
+ end
34
+
35
+ def titre(commit) = commit.dig("commit", "message")[/.*/]
36
+ end
37
+ end
@@ -23,15 +23,22 @@ module CaptiveRelease
23
23
  @http = http || GithubHttp.new(token: token)
24
24
  end
25
25
 
26
+ def get(chemin) = analyser(@http.get(adresse(chemin)), "200")
27
+
26
28
  def post(chemin, corps, attendu:)
27
- reponse = @http.post(URI("#{HOTE}/#{depot.path(chemin)}"), JSON.generate(corps))
29
+ analyser(@http.post(adresse(chemin), JSON.generate(corps)), attendu)
30
+ end
31
+
32
+ private
33
+
34
+ def adresse(chemin) = URI("#{HOTE}/#{depot.path(chemin)}")
35
+
36
+ def analyser(reponse, attendu)
28
37
  refuser(reponse) unless reponse.code == attendu
29
38
 
30
39
  reponse.body.to_s.empty? ? nil : JSON.parse(reponse.body)
31
40
  end
32
41
 
33
- private
34
-
35
42
  def refuser(reponse)
36
43
  raise Messages.t(:github_api_error, code: reponse.code, message: GithubErrorMessage.from(reponse.body.to_s))
37
44
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "github_api"
4
+ require_relative "direct_commits"
5
+ require_relative "release_notes"
4
6
 
5
7
  module CaptiveRelease
6
8
  # Crée la release GitHub d'une version. Elle compose ce qu'elle demande ; l'API, son
@@ -9,7 +11,6 @@ module CaptiveRelease
9
11
  # GitHub refuse (422) un corps de release plus long. Sans tag précédent, les notes
10
12
  # générées couvrent tout l'historique et dépassent vite cette limite.
11
13
  CORPS_MAX = 125_000
12
- COUPURE = "\n\n…"
13
14
 
14
15
  def self.create(version:, remote_url:, token:, api: nil)
15
16
  api ||= GithubApi.new(remote_url: remote_url, token: token)
@@ -35,21 +36,7 @@ module CaptiveRelease
35
36
 
36
37
  def notes(tag)
37
38
  corps = @api.post("releases/generate-notes", { "tag_name" => tag }, attendu: "200")["body"].to_s
38
- corps.length > CORPS_MAX ? tronquer(corps) : corps
39
- end
40
-
41
- # Coupe à une fin de ligne et garde le lien Full Changelog final, qui mène à la liste complète.
42
- def tronquer(corps)
43
- tete, pied = separer_pied(corps)
44
- place = CORPS_MAX - COUPURE.length - pied.length
45
- coupe = tete[0, place]
46
- coupe = coupe[0, coupe.rindex("\n")] if coupe.include?("\n")
47
- "#{coupe}#{COUPURE}#{pied}"
48
- end
49
-
50
- def separer_pied(corps)
51
- index = corps.rindex("\n**Full Changelog**")
52
- index ? [ corps[0, index], "\n#{corps[index..]}" ] : [ corps, "" ]
39
+ DirectCommits.new(@api).completer(ReleaseNotes.new(corps)).tronquer(CORPS_MAX).to_s
53
40
  end
54
41
  end
55
42
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CaptiveRelease
4
+ # Le texte des notes d'une release, découpé autour de son pied : le lien Full Changelog,
5
+ # qui mène à la liste complète et doit survivre à tout ajout ou toute coupe.
6
+ class ReleaseNotes
7
+ PIED = /(?:\n|\A)\*\*Full Changelog\*\*/
8
+ COMPARAISON = %r{/compare/(?<depuis>\S+?)\.\.\.(?<jusqu_a>\S+)}
9
+ COUPURE = "\n\n…"
10
+
11
+ def initialize(corps)
12
+ index = corps.rindex(PIED)
13
+ @tete, @pied = index ? [ corps[0, index], corps[index..] ] : [ corps, "" ]
14
+ end
15
+
16
+ # Les tags qui bornent les notes ; nil sans tag précédent.
17
+ def plage = @pied.match(COMPARAISON)&.captures
18
+
19
+ def ajouter(section) = ReleaseNotes.new("#{@tete.rstrip}\n\n#{section}#{@pied}")
20
+
21
+ # Coupe à une fin de ligne, pour ne pas laisser un lien ou un émoji à moitié.
22
+ def tronquer(max)
23
+ return self if to_s.length <= max
24
+
25
+ coupe = @tete[0, max - COUPURE.length - @pied.length - 1]
26
+ coupe = coupe[0, coupe.rindex("\n")] if coupe.include?("\n")
27
+ ReleaseNotes.new("#{coupe}#{COUPURE}\n#{@pied}")
28
+ end
29
+
30
+ def to_s = "#{@tete}#{@pied}"
31
+ end
32
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CaptiveRelease
4
- VERSION = "0.11.0"
4
+ VERSION = "0.12.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: captive-release
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Captive Studio
@@ -33,6 +33,7 @@ files:
33
33
  - lib/captive_release/config_file.rb
34
34
  - lib/captive_release/config_loader.rb
35
35
  - lib/captive_release/config_writer.rb
36
+ - lib/captive_release/direct_commits.rb
36
37
  - lib/captive_release/exclusion_input.rb
37
38
  - lib/captive_release/framework_detector.rb
38
39
  - lib/captive_release/gemfile_lock_refresh.rb
@@ -58,6 +59,7 @@ files:
58
59
  - lib/captive_release/prerelease_iteration.rb
59
60
  - lib/captive_release/release_command.rb
60
61
  - lib/captive_release/release_confirmation.rb
62
+ - lib/captive_release/release_notes.rb
61
63
  - lib/captive_release/release_overview.rb
62
64
  - lib/captive_release/release_preconditions.rb
63
65
  - lib/captive_release/release_publication.rb