captive-release 0.5.0 → 0.7.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/lib/captive_release/bump_safety_net.rb +31 -0
- data/lib/captive_release/bump_severity.rb +9 -0
- data/lib/captive_release/bump_suggestion.rb +15 -0
- data/lib/captive_release/gemfile_lock_refresh.rb +30 -0
- data/lib/captive_release/git_commands.rb +6 -0
- data/lib/captive_release/gitmoji_severity.rb +25 -0
- data/lib/captive_release/release_command.rb +26 -3
- data/lib/captive_release/terminal.rb +10 -7
- data/lib/captive_release/version.rb +1 -1
- data/lib/captive_release/version_severity.rb +19 -0
- data/lib/captive_release.rb +6 -0
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 51e98c43b123462f3166f453ca8623c9b748fff59c11259840ea5c5e13ead96b
|
|
4
|
+
data.tar.gz: db1e3f7f2e542786774b8eb72f4501196cccfe5b971a4d4c29a9f3ce6cf9f243
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 396e2ba7f46def9bde2aeba4afdcfefe91e9a0ba4d028e29f5f0881e5c3c4f32dfe3c753a3cd493b78fa80c1e81283ceea9ec2b593a2ffe8ecce7b593a08195f
|
|
7
|
+
data.tar.gz: da82b40b4abb5f32649022dcf1f54b13c769a5ae046f097768b54b422588fae14154ddbe1b260f676a65c2d8e438efd73cc13e1c4dcbd0def640503c6abdff29
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CaptiveRelease
|
|
4
|
+
# Le garde-fou : si la version choisie est d'une sévérité inférieure à ce que les commits
|
|
5
|
+
# suggèrent, impose une confirmation explicite avant de continuer. Contrairement à
|
|
6
|
+
# confirmer!, une entrée vide refuse : c'est un avertissement, pas la confirmation normale.
|
|
7
|
+
class BumpSafetyNet
|
|
8
|
+
def initialize(terminal:)
|
|
9
|
+
@terminal = terminal
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def verifier!(suggestion, courante, prochaine)
|
|
13
|
+
return unless suggestion
|
|
14
|
+
|
|
15
|
+
choisie = VersionSeverity.classify(from: courante, to: prochaine)
|
|
16
|
+
return if BumpSeverity::RANK.fetch(choisie, -1) >= BumpSeverity::RANK.fetch(suggestion)
|
|
17
|
+
|
|
18
|
+
avertir_et_confirmer!(suggestion, choisie)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def avertir_et_confirmer!(suggestion, choisie)
|
|
24
|
+
@terminal.say @terminal.warn(
|
|
25
|
+
"\n⚠️ Les commits suggèrent un bump #{suggestion}, mais la version choisie n'est qu'un #{choisie}.",
|
|
26
|
+
)
|
|
27
|
+
reponse = @terminal.ask("Continuer quand même ? [y/N] ").downcase
|
|
28
|
+
exit 1 unless reponse == "y"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CaptiveRelease
|
|
4
|
+
# L'ordre de gravité partagé entre la suggestion (gitmoji) et le garde-fou (semver choisi) :
|
|
5
|
+
# dit ce qui prime quand les signaux sont mêlés, patch < minor < major.
|
|
6
|
+
module BumpSeverity
|
|
7
|
+
RANK = { "patch" => 0, "minor" => 1, "major" => 2 }.freeze
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CaptiveRelease
|
|
4
|
+
# La sévérité de bump suggérée par les commits depuis le dernier tag - nil tant que ce tag
|
|
5
|
+
# n'existe pas, comme ChangesSince pour le lien de comparaison.
|
|
6
|
+
class BumpSuggestion
|
|
7
|
+
def self.for(version)
|
|
8
|
+
return nil unless GitCommands.tag?("v#{version}")
|
|
9
|
+
|
|
10
|
+
GitmojiSeverity.for(GitCommands.messages_since("v#{version}"))
|
|
11
|
+
rescue StandardError
|
|
12
|
+
nil
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CaptiveRelease
|
|
4
|
+
# Remet Gemfile.lock d'aplomb après un bump : un .gemspec à la racine porte la version
|
|
5
|
+
# bumpée, donc Gemfile.lock devient périmé et --deployment le refuse en CI si personne ne
|
|
6
|
+
# le relance.
|
|
7
|
+
#
|
|
8
|
+
# Cette unité change quand la façon de détecter ou de relancer le lock change,
|
|
9
|
+
# ReleaseCommand quand la suite des étapes change.
|
|
10
|
+
class GemfileLockRefresh
|
|
11
|
+
LOCKFILE = "Gemfile.lock"
|
|
12
|
+
|
|
13
|
+
def initialize(files: RepoFiles.new("."))
|
|
14
|
+
@files = files
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# rescue : une arborescence hostile ne doit jamais faire échouer la release, la détection
|
|
18
|
+
# n'est qu'une commodité. Une fois vraie, l'exécution du bundle lock, elle, échoue haut
|
|
19
|
+
# la main si elle rate.
|
|
20
|
+
def needed?
|
|
21
|
+
File.exist?(@files.path(LOCKFILE)) && @files.glob("*.gemspec").any?
|
|
22
|
+
rescue StandardError
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def call
|
|
27
|
+
Shell.run! "bundle lock"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -12,6 +12,12 @@ module CaptiveRelease
|
|
|
12
12
|
|
|
13
13
|
def self.tag?(nom) = !Shell.capture("git tag -l #{nom} 2>/dev/null").empty?
|
|
14
14
|
|
|
15
|
+
# Les sujets de commit depuis le tag donné jusqu'à HEAD - la matière première de la
|
|
16
|
+
# suggestion de bump, un message par ligne, propre de toute ligne vide.
|
|
17
|
+
def self.messages_since(tag)
|
|
18
|
+
Shell.capture("git log --pretty=%s #{Shell.escape(tag)}..HEAD").lines.map(&:strip).reject(&:empty?)
|
|
19
|
+
end
|
|
20
|
+
|
|
15
21
|
def self.current_branch
|
|
16
22
|
branche = Shell.capture("git rev-parse --abbrev-ref HEAD 2>/dev/null")
|
|
17
23
|
branche.empty? ? "main" : branche
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CaptiveRelease
|
|
4
|
+
# Traduit une liste de messages de commit en sévérité de bump suggérée : 💥 casse, ✨
|
|
5
|
+
# ajoute, tout le reste est un patch par défaut. Signaux mêlés : le plus sévère gagne.
|
|
6
|
+
class GitmojiSeverity
|
|
7
|
+
MAJOR = "💥"
|
|
8
|
+
MINOR = "✨"
|
|
9
|
+
|
|
10
|
+
def self.for(messages)
|
|
11
|
+
messages.reduce("patch") { |pire, message| plus_severe(pire, severity_of(message)) }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.severity_of(message)
|
|
15
|
+
return "major" if message.start_with?(MAJOR)
|
|
16
|
+
return "minor" if message.start_with?(MINOR)
|
|
17
|
+
|
|
18
|
+
"patch"
|
|
19
|
+
end
|
|
20
|
+
private_class_method :severity_of
|
|
21
|
+
|
|
22
|
+
def self.plus_severe(a, b) = BumpSeverity::RANK[a] >= BumpSeverity::RANK[b] ? a : b
|
|
23
|
+
private_class_method :plus_severe
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -18,23 +18,46 @@ module CaptiveRelease
|
|
|
18
18
|
@terminal.say "\n#{@terminal.bold("captive-release")}\n"
|
|
19
19
|
@terminal.say "Current version: #{@terminal.bold(courante)}\n"
|
|
20
20
|
afficher_changements(courante)
|
|
21
|
+
suggestion = BumpSuggestion.for(courante)
|
|
22
|
+
afficher_suggestion(suggestion)
|
|
21
23
|
|
|
22
24
|
prochaine = VersionChoice.new(terminal: @terminal).call(courante, bump_arg)
|
|
23
|
-
|
|
25
|
+
if confirmation
|
|
26
|
+
BumpSafetyNet.new(terminal: @terminal).verifier!(suggestion, courante, prochaine)
|
|
27
|
+
confirmer!(prochaine)
|
|
28
|
+
end
|
|
24
29
|
|
|
25
30
|
VersionFilesWriting.new(terminal: @terminal).call(prochaine, @config.version_files)
|
|
26
|
-
GitRelease.new(terminal: @terminal).call(prochaine,
|
|
31
|
+
GitRelease.new(terminal: @terminal).call(prochaine, chemins_a_commiter)
|
|
27
32
|
publication.call(prochaine)
|
|
28
33
|
@terminal.say "\n🎉 #{@terminal.bold("Version v#{prochaine} released successfully!")}\n"
|
|
29
34
|
end
|
|
30
35
|
|
|
31
36
|
private
|
|
32
37
|
|
|
38
|
+
def chemins_a_commiter
|
|
39
|
+
chemins = @config.version_files.map(&:path)
|
|
40
|
+
return chemins unless (refresh = GemfileLockRefresh.new).needed?
|
|
41
|
+
|
|
42
|
+
@terminal.say "\n#{@terminal.bold("Refreshing Gemfile.lock...")}"
|
|
43
|
+
refresh.call
|
|
44
|
+
@terminal.done GemfileLockRefresh::LOCKFILE
|
|
45
|
+
|
|
46
|
+
chemins + [ GemfileLockRefresh::LOCKFILE ]
|
|
47
|
+
end
|
|
48
|
+
|
|
33
49
|
def afficher_changements(courante)
|
|
34
50
|
lien = ChangesSince.url(courante)
|
|
35
51
|
return unless lien
|
|
36
52
|
|
|
37
|
-
@terminal.say "Changement : #{@terminal.link(lien)}\n"
|
|
53
|
+
@terminal.say "Changement : #{@terminal.link(lien)}\n\n"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def afficher_suggestion(suggestion)
|
|
57
|
+
return unless suggestion
|
|
58
|
+
|
|
59
|
+
detail = { "major" => "💥 détecté", "minor" => "✨ détecté", "patch" => "aucun commit notable détecté" }
|
|
60
|
+
@terminal.say "Suggestion : #{suggestion} (#{detail.fetch(suggestion)})\n"
|
|
38
61
|
end
|
|
39
62
|
|
|
40
63
|
def confirmer!(version)
|
|
@@ -29,12 +29,15 @@ module CaptiveRelease
|
|
|
29
29
|
def done(etape) = say(" #{ok("✅")} #{etape}")
|
|
30
30
|
|
|
31
31
|
# OSC 8 : le terminal affiche `texte` mais ouvre `url` au clic, sans exposer l'URL brute.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
def
|
|
37
|
-
def
|
|
38
|
-
def
|
|
32
|
+
# Le texte est souligné pour signaler qu'il est cliquable, certains terminaux ne le faisant
|
|
33
|
+
# pas eux-mêmes pour les liens OSC 8.
|
|
34
|
+
def link(url, texte = url) = "\e]8;;#{url}\e\\#{underline(texte)}\e]8;;\e\\"
|
|
35
|
+
|
|
36
|
+
def bold(str) = "\e[1m#{str}\e[0m"
|
|
37
|
+
def underline(str) = "\e[4m#{str}\e[0m"
|
|
38
|
+
def dim(str) = "\e[2m#{str}\e[0m"
|
|
39
|
+
def ok(str) = "\e[32m#{str}\e[0m"
|
|
40
|
+
def warn(str) = "\e[33m#{str}\e[0m"
|
|
41
|
+
def error(str) = "\e[31m#{str}\e[0m"
|
|
39
42
|
end
|
|
40
43
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CaptiveRelease
|
|
4
|
+
# Classe la taille réelle d'un bump en comparant les composants major.minor.patch entre
|
|
5
|
+
# deux versions - y compris un semver tapé à la main, qui ne porte plus de "type" une fois
|
|
6
|
+
# sorti de VersionChoice.
|
|
7
|
+
class VersionSeverity
|
|
8
|
+
def self.classify(from:, to:)
|
|
9
|
+
depart = from.split(".").map(&:to_i)
|
|
10
|
+
arrivee = to.split(".").map(&:to_i)
|
|
11
|
+
|
|
12
|
+
return "major" if depart[0] != arrivee[0]
|
|
13
|
+
return "minor" if depart[1] != arrivee[1]
|
|
14
|
+
return "patch" if depart[2] != arrivee[2]
|
|
15
|
+
|
|
16
|
+
"other"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/captive_release.rb
CHANGED
|
@@ -12,6 +12,11 @@ require_relative "captive_release/framework_detector"
|
|
|
12
12
|
require_relative "captive_release/version_detector"
|
|
13
13
|
require_relative "captive_release/shell"
|
|
14
14
|
require_relative "captive_release/git_commands"
|
|
15
|
+
require_relative "captive_release/bump_severity"
|
|
16
|
+
require_relative "captive_release/gitmoji_severity"
|
|
17
|
+
require_relative "captive_release/bump_suggestion"
|
|
18
|
+
require_relative "captive_release/version_severity"
|
|
19
|
+
require_relative "captive_release/bump_safety_net"
|
|
15
20
|
require_relative "captive_release/github_repo"
|
|
16
21
|
require_relative "captive_release/github_http"
|
|
17
22
|
require_relative "captive_release/github_api"
|
|
@@ -31,6 +36,7 @@ require_relative "captive_release/build_dispatch_run"
|
|
|
31
36
|
require_relative "captive_release/build_command"
|
|
32
37
|
require_relative "captive_release/menu"
|
|
33
38
|
require_relative "captive_release/version_choice"
|
|
39
|
+
require_relative "captive_release/gemfile_lock_refresh"
|
|
34
40
|
require_relative "captive_release/git_release"
|
|
35
41
|
require_relative "captive_release/changes_since"
|
|
36
42
|
require_relative "captive_release/release_publication"
|
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.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Captive Studio
|
|
@@ -24,6 +24,9 @@ files:
|
|
|
24
24
|
- lib/captive_release/build_config.rb
|
|
25
25
|
- lib/captive_release/build_config_prompt.rb
|
|
26
26
|
- lib/captive_release/build_dispatch_run.rb
|
|
27
|
+
- lib/captive_release/bump_safety_net.rb
|
|
28
|
+
- lib/captive_release/bump_severity.rb
|
|
29
|
+
- lib/captive_release/bump_suggestion.rb
|
|
27
30
|
- lib/captive_release/changes_since.rb
|
|
28
31
|
- lib/captive_release/cli.rb
|
|
29
32
|
- lib/captive_release/config.rb
|
|
@@ -31,6 +34,7 @@ files:
|
|
|
31
34
|
- lib/captive_release/config_loader.rb
|
|
32
35
|
- lib/captive_release/config_writer.rb
|
|
33
36
|
- lib/captive_release/framework_detector.rb
|
|
37
|
+
- lib/captive_release/gemfile_lock_refresh.rb
|
|
34
38
|
- lib/captive_release/git_commands.rb
|
|
35
39
|
- lib/captive_release/git_release.rb
|
|
36
40
|
- lib/captive_release/github_api.rb
|
|
@@ -39,6 +43,7 @@ files:
|
|
|
39
43
|
- lib/captive_release/github_release.rb
|
|
40
44
|
- lib/captive_release/github_repo.rb
|
|
41
45
|
- lib/captive_release/github_token.rb
|
|
46
|
+
- lib/captive_release/gitmoji_severity.rb
|
|
42
47
|
- lib/captive_release/help.rb
|
|
43
48
|
- lib/captive_release/init_command.rb
|
|
44
49
|
- lib/captive_release/menu.rb
|
|
@@ -59,6 +64,7 @@ files:
|
|
|
59
64
|
- lib/captive_release/version_files_argument.rb
|
|
60
65
|
- lib/captive_release/version_files_writing.rb
|
|
61
66
|
- lib/captive_release/version_format.rb
|
|
67
|
+
- lib/captive_release/version_severity.rb
|
|
62
68
|
homepage: https://github.com/captive-studio/captive-release
|
|
63
69
|
licenses:
|
|
64
70
|
- MIT
|