captive-release 0.7.1 → 0.7.2

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/captive_release/build_command.rb +4 -4
  3. data/lib/captive_release/build_config_prompt.rb +6 -5
  4. data/lib/captive_release/build_dispatch_run.rb +5 -5
  5. data/lib/captive_release/bump_safety_net.rb +2 -5
  6. data/lib/captive_release/cli.rb +1 -1
  7. data/lib/captive_release/config.rb +1 -1
  8. data/lib/captive_release/config_file.rb +1 -1
  9. data/lib/captive_release/config_loader.rb +3 -5
  10. data/lib/captive_release/config_writer.rb +4 -5
  11. data/lib/captive_release/git_release.rb +1 -1
  12. data/lib/captive_release/github_api.rb +4 -10
  13. data/lib/captive_release/github_error_message.rb +35 -0
  14. data/lib/captive_release/github_http.rb +2 -2
  15. data/lib/captive_release/github_release.rb +29 -2
  16. data/lib/captive_release/github_repo.rb +1 -1
  17. data/lib/captive_release/help.rb +6 -36
  18. data/lib/captive_release/init_command.rb +3 -3
  19. data/lib/captive_release/locale.rb +30 -0
  20. data/lib/captive_release/locales/en.yml +106 -0
  21. data/lib/captive_release/locales/fr.yml +106 -0
  22. data/lib/captive_release/menu.rb +1 -1
  23. data/lib/captive_release/messages.rb +31 -0
  24. data/lib/captive_release/release_command.rb +4 -31
  25. data/lib/captive_release/release_confirmation.rb +21 -0
  26. data/lib/captive_release/release_overview.rb +40 -0
  27. data/lib/captive_release/release_preconditions.rb +2 -2
  28. data/lib/captive_release/release_publication.rb +2 -2
  29. data/lib/captive_release/shell.rb +1 -1
  30. data/lib/captive_release/version.rb +1 -1
  31. data/lib/captive_release/version_bumper.rb +2 -2
  32. data/lib/captive_release/version_choice.rb +5 -5
  33. data/lib/captive_release/version_file.rb +3 -3
  34. data/lib/captive_release/version_file_bootstrap.rb +3 -3
  35. data/lib/captive_release/version_file_selection.rb +3 -3
  36. data/lib/captive_release/version_files_writing.rb +1 -1
  37. data/lib/captive_release.rb +3 -0
  38. metadata +23 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 29aedc00590a519b9e739e62c78da5bb51369ef22871217b1d51b384584e6375
4
- data.tar.gz: a52702e1b4cc59ae94b3baac8fe9a87a27e1fd0c76e715e212c67b540708f81a
3
+ metadata.gz: e40b1f1d86f5fd4ac74c2567a5c08ca52038229e77fdd2e9eedfb16d20d0edd7
4
+ data.tar.gz: 388299f2cc9531dd30dc47fcd83f49ebe841929a5f3fb379928e18d83f65f7a8
5
5
  SHA512:
6
- metadata.gz: 1cb2a9558aceaef25bf15340bc0918259b942bbb6c91a4b8a7311740441da5a7b6e7bccf1b5c53b7dc66a6d05c18c68f2627eabe0740ae6a73458fbc4ccc4eec
7
- data.tar.gz: 7cadc0350772b0d766a27018fdca30b83a71d75e8c6096ee31918768a9e8d15136528689b37ca32d0ec591f34dab8c9356f518475652238943197930420061da
6
+ metadata.gz: dca417210074ab658e94f172ecbd2ac5f19568ce9dd99416a7bc99d7ae5cab57e0ea9c6c7bc19bff36bdfc78fff61184a0159cb370caacc20994da6d793af96e
7
+ data.tar.gz: 2e30da7a1d7db50c419f66cef9f479b5384590253802553bfd9a425d00443fb9da212240cbcd4ec69e92100478d1f843b48549d076e771883bdc8b1b8452a292
@@ -26,8 +26,8 @@ module CaptiveRelease
26
26
  def refuser_sans_configuration
27
27
  return if @config.expo? || @config.capacitor?
28
28
 
29
- @terminal.say @terminal.error("Aucune configuration de build dans #{CONFIG_FILE}.")
30
- @terminal.say "Lancez #{@terminal.bold("captive-release init")} pour configurer le workflow de build.\n\n"
29
+ @terminal.say @terminal.error(Messages.t(:no_build_config, file: CONFIG_FILE))
30
+ @terminal.say "#{Messages.t(:run_init_for_build, command: @terminal.bold("captive-release init"))}\n\n"
31
31
  exit 1
32
32
  end
33
33
 
@@ -35,12 +35,12 @@ module CaptiveRelease
35
35
  jeton = @token_provider.call
36
36
  return jeton if jeton
37
37
 
38
- @terminal.say @terminal.error("GITHUB_TOKEN or `gh auth login` required to dispatch build workflows.")
38
+ @terminal.say @terminal.error(Messages.t(:build_token_required))
39
39
  exit 1
40
40
  end
41
41
 
42
42
  def demander_le_profil
43
- Menu.new(terminal: @terminal).choose("Choose profile:",
43
+ Menu.new(terminal: @terminal).choose(Messages.t(:choose_profile),
44
44
  { "staging" => "staging",
45
45
  "production" => "production", })
46
46
  end
@@ -11,10 +11,10 @@ module CaptiveRelease
11
11
  end
12
12
 
13
13
  def call(framework, workflow_par_defaut)
14
- @terminal.say "\n#{@terminal.bold("#{framework} project detected.")} " \
15
- "Configure build dispatch?\n"
16
- @terminal.say " #{@terminal.dim("1)")} Yes"
17
- @terminal.say " #{@terminal.dim("2)")} No"
14
+ @terminal.say "\n#{@terminal.bold(Messages.t(:framework_detected, framework: framework))} " \
15
+ "#{Messages.t(:configure_build)}\n"
16
+ @terminal.say " #{@terminal.dim("1)")} #{Messages.t(:yes)}"
17
+ @terminal.say " #{@terminal.dim("2)")} #{Messages.t(:no)}"
18
18
  return nil unless @terminal.ask_choice == 1
19
19
 
20
20
  BuildConfig.new(build_workflow: workflow(workflow_par_defaut))
@@ -23,7 +23,8 @@ module CaptiveRelease
23
23
  private
24
24
 
25
25
  def workflow(workflow_par_defaut)
26
- @terminal.say "\nWorkflow file name #{@terminal.dim("(default: #{workflow_par_defaut})")}:"
26
+ par_defaut = @terminal.dim(Messages.t(:workflow_default, workflow: workflow_par_defaut))
27
+ @terminal.say "\n#{Messages.t(:workflow_file_name, hint: par_defaut)}"
27
28
  saisie = @terminal.ask
28
29
  saisie.empty? ? workflow_par_defaut : saisie
29
30
  end
@@ -19,17 +19,17 @@ module CaptiveRelease
19
19
  inputs: { "profile" => profil },
20
20
  remote_url: GitCommands.remote_url, token: jeton)
21
21
 
22
- @terminal.done "Build dispatched!"
22
+ @terminal.done Messages.t(:build_dispatched)
23
23
  @terminal.say " #{url}\n"
24
24
  end
25
25
 
26
26
  private
27
27
 
28
28
  def annoncer(profil, workflow, branche)
29
- @terminal.say "Profile: #{@terminal.bold(profil)}"
30
- @terminal.say "Workflow: #{@terminal.bold(workflow)}"
31
- @terminal.say "Branch: #{@terminal.bold(branche)}"
32
- @terminal.say "\nDispatching...\n"
29
+ @terminal.say Messages.t(:dispatch_profile, value: @terminal.bold(profil))
30
+ @terminal.say Messages.t(:dispatch_workflow, value: @terminal.bold(workflow))
31
+ @terminal.say Messages.t(:dispatch_branch, value: @terminal.bold(branche))
32
+ @terminal.say "\n#{Messages.t(:dispatching)}\n"
33
33
  end
34
34
  end
35
35
  end
@@ -21,11 +21,8 @@ module CaptiveRelease
21
21
  private
22
22
 
23
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"
24
+ @terminal.say @terminal.warn("\n#{Messages.t(:bump_warning, suggestion: suggestion, chosen: choisie)}")
25
+ exit 1 unless Messages.yes?(@terminal.ask(Messages.t(:continue_anyway)))
29
26
  end
30
27
  end
31
28
  end
@@ -52,7 +52,7 @@ module CaptiveRelease
52
52
  end
53
53
 
54
54
  def refuser(command)
55
- terminal.say terminal.error("Unknown command: #{command}")
55
+ terminal.say terminal.error(Messages.t(:unknown_command, command: command))
56
56
  aide.show
57
57
  exit 1
58
58
  end
@@ -9,7 +9,7 @@ module CaptiveRelease
9
9
  attr_reader :version_files, :expo, :capacitor
10
10
 
11
11
  def initialize(version_files, expo = nil, capacitor = nil)
12
- raise ArgumentError, "version_files cannot be empty" if version_files.empty?
12
+ raise ArgumentError, Messages.t(:version_files_empty) if version_files.empty?
13
13
 
14
14
  @version_files = version_files
15
15
  @expo = expo
@@ -10,7 +10,7 @@ module CaptiveRelease
10
10
  # configuration change.
11
11
  module ConfigFile
12
12
  def self.load(path = CONFIG_FILE)
13
- raise "#{CONFIG_FILE} not found. Run `captive-release init` first." unless File.exist?(path)
13
+ raise Messages.t(:config_not_found, file: CONFIG_FILE) unless File.exist?(path)
14
14
 
15
15
  data = JSON.parse(File.read(path))
16
16
  Config.new(
@@ -23,11 +23,9 @@ module CaptiveRelease
23
23
  private
24
24
 
25
25
  def refuser
26
- @terminal.say @terminal.error(
27
- "Aucun fichier de version trouvé (package.json, lib/**/version.rb, VERSION).",
28
- )
29
- @terminal.say "Créez un fichier #{@terminal.bold("VERSION")} " \
30
- "ou lancez #{@terminal.bold("captive-release init")}.\n"
26
+ @terminal.say @terminal.error(Messages.t(:no_version_file))
27
+ @terminal.say "#{Messages.t(:create_version_or_init, file: @terminal.bold("VERSION"),
28
+ command: @terminal.bold("captive-release init"))}\n"
31
29
  exit 1
32
30
  end
33
31
  end
@@ -25,17 +25,16 @@ module CaptiveRelease
25
25
  private
26
26
 
27
27
  def refuser_le_vide
28
- @terminal.say @terminal.error("No files selected.")
28
+ @terminal.say @terminal.error(Messages.t(:no_files_selected))
29
29
  exit 1
30
30
  end
31
31
 
32
32
  def annoncer(fichiers, expo, capacitor)
33
- @terminal.say "\n#{@terminal.ok("✅ captive-release.json written with " \
34
- "#{fichiers.size} version file(s).")}"
35
- @terminal.say " #{@terminal.dim("Expo build workflow: #{expo.build_workflow}")}" if expo
33
+ @terminal.say "\n#{@terminal.ok(Messages.t(:config_written, count: fichiers.size))}"
34
+ @terminal.say " #{@terminal.dim(Messages.t(:expo_workflow, workflow: expo.build_workflow))}" if expo
36
35
  return unless capacitor
37
36
 
38
- @terminal.say " #{@terminal.dim("Capacitor build workflow: #{capacitor.build_workflow}")}"
37
+ @terminal.say " #{@terminal.dim(Messages.t(:capacitor_workflow, workflow: capacitor.build_workflow))}"
39
38
  end
40
39
 
41
40
  def demander(framework, workflow_par_defaut)
@@ -12,7 +12,7 @@ module CaptiveRelease
12
12
  end
13
13
 
14
14
  def call(version, paths)
15
- @terminal.say "\n#{@terminal.bold("Running git commands...")}"
15
+ @terminal.say "\n#{@terminal.bold(Messages.t(:running_git))}"
16
16
  GitCommands.add_and_commit(version, paths)
17
17
  @terminal.done %(git commit "🔖 Set version v#{version}")
18
18
  GitCommands.tag(version)
@@ -4,14 +4,15 @@ require "json"
4
4
  require "uri"
5
5
  require_relative "github_http"
6
6
  require_relative "github_repo"
7
+ require_relative "github_error_message"
7
8
 
8
9
  module CaptiveRelease
9
10
  # L'API GitHub d'un dépôt : à quelle adresse poster, et ce qui compte comme un échec. Les
10
11
  # commandes lui parlent par délégation plutôt que d'en hériter le transport, ce qui leur
11
12
  # laisse la seule composition de leur payload.
12
13
  #
13
- # Cette unité change quand l'adressage ou la politique d'erreur changent, GithubHttp quand
14
- # le transport change.
14
+ # Cette unité change quand l'adressage ou ce qui compte comme un échec changent, GithubHttp
15
+ # quand le transport change, GithubErrorMessage quand la forme des erreurs change.
15
16
  class GithubApi
16
17
  HOTE = "https://api.github.com"
17
18
 
@@ -31,15 +32,8 @@ module CaptiveRelease
31
32
 
32
33
  private
33
34
 
34
- # Le corps d'une erreur GitHub est du JSON qui nomme la cause, sauf quand il n'en est pas :
35
- # un 500 rend parfois du texte brut, qu'il vaut mieux montrer que d'avaler.
36
35
  def refuser(reponse)
37
- data = begin
38
- JSON.parse(reponse.body)
39
- rescue StandardError
40
- {}
41
- end
42
- raise "GitHub API error (#{reponse.code}): #{data["message"] || reponse.body}"
36
+ raise Messages.t(:github_api_error, code: reponse.code, message: GithubErrorMessage.from(reponse.body.to_s))
43
37
  end
44
38
  end
45
39
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module CaptiveRelease
6
+ # Ce qu'un corps d'erreur GitHub dit de la cause. Le corps est du JSON qui la nomme, sauf
7
+ # quand il n'en est pas : un 500 rend parfois du texte brut, qu'il vaut mieux montrer que
8
+ # d'avaler. Et un 422 ne dit que « Validation Failed » : la cause réelle est dans `errors`.
9
+ #
10
+ # Cette unité change quand GitHub change la forme de ses erreurs, GithubApi quand ce qui
11
+ # compte comme un échec change.
12
+ module GithubErrorMessage
13
+ def self.from(brut)
14
+ data = analyser(brut)
15
+ details = Array(data["errors"]).map { |erreur| detail(erreur) }.reject(&:empty?)
16
+ principal = data["message"] || brut
17
+ details.empty? ? principal : "#{principal} — #{details.join("; ")}"
18
+ end
19
+
20
+ def self.analyser(brut)
21
+ data = JSON.parse(brut)
22
+ data.is_a?(Hash) ? data : {}
23
+ rescue StandardError
24
+ {}
25
+ end
26
+ private_class_method :analyser
27
+
28
+ def self.detail(erreur)
29
+ return erreur.to_s unless erreur.is_a?(Hash)
30
+
31
+ erreur["message"] || [ erreur["field"], erreur["code"] ].compact.join(" ")
32
+ end
33
+ private_class_method :detail
34
+ end
35
+ end
@@ -28,7 +28,7 @@ module CaptiveRelease
28
28
  uri = URI(ailleurs!(reponse))
29
29
  end
30
30
 
31
- raise "Too many redirects from GitHub API"
31
+ raise Messages.t(:too_many_redirects)
32
32
  end
33
33
 
34
34
  private
@@ -45,7 +45,7 @@ module CaptiveRelease
45
45
 
46
46
  def ailleurs!(reponse)
47
47
  reponse["location"] || reponse["Location"] ||
48
- raise("GitHub redirect without Location header")
48
+ raise(Messages.t(:redirect_without_location))
49
49
  end
50
50
  end
51
51
  end
@@ -6,6 +6,11 @@ module CaptiveRelease
6
6
  # Crée la release GitHub d'une version. Elle compose ce qu'elle demande ; l'API, son
7
7
  # authentification et ses échecs vivent dans GithubApi.
8
8
  class GithubRelease
9
+ # GitHub refuse (422) un corps de release plus long. Sans tag précédent, les notes
10
+ # générées couvrent tout l'historique et dépassent vite cette limite.
11
+ CORPS_MAX = 125_000
12
+ COUPURE = "\n\n…"
13
+
9
14
  def self.create(version:, remote_url:, token:, api: nil)
10
15
  api ||= GithubApi.new(remote_url: remote_url, token: token)
11
16
  new(api).create(version)
@@ -19,10 +24,32 @@ module CaptiveRelease
19
24
  # composer ici obligeait à lister les commits, et sans tag précédent cette liste était
20
25
  # l'historique entier du dépôt.
21
26
  def create(version)
27
+ tag = "v#{version}"
22
28
  @api.post("releases",
23
- { "tag_name" => "v#{version}", "name" => "v#{version}",
24
- "generate_release_notes" => true, "draft" => false, "prerelease" => false, },
29
+ { "tag_name" => tag, "name" => tag, "body" => notes(tag),
30
+ "draft" => false, "prerelease" => false, },
25
31
  attendu: "201")["html_url"]
26
32
  end
33
+
34
+ private
35
+
36
+ def notes(tag)
37
+ 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, "" ]
53
+ end
27
54
  end
28
55
  end
@@ -12,7 +12,7 @@ module CaptiveRelease
12
12
 
13
13
  def self.from_remote(url)
14
14
  match = url.match(REMOTE)
15
- raise "Cannot parse GitHub repo from remote URL: #{url}" unless match
15
+ raise Messages.t(:cannot_parse_remote, url: url) unless match
16
16
 
17
17
  new(match[1], match[2])
18
18
  end
@@ -3,48 +3,18 @@
3
3
  module CaptiveRelease
4
4
  # Ce que la CLI documente d'elle-même. Cette unité change quand la documentation de
5
5
  # l'interface change, la CLI quand son aiguillage change.
6
+ #
7
+ # L'aide est un seul texte par langue, que le traducteur lit d'un bloc : les titres s'y
8
+ # reconnaissent à ce qu'ils ne sont pas indentés et finissent par deux-points.
6
9
  class Help
10
+ TITRES = /\Acaptive-release|^\S.*:$/
11
+
7
12
  def initialize(terminal:)
8
13
  @terminal = terminal
9
14
  end
10
15
 
11
16
  def show
12
- @terminal.say <<~HELP
13
-
14
- #{@terminal.bold("captive-release")} : Semver release tool
15
-
16
- #{@terminal.bold("Usage:")}
17
- captive-release init [--files path[:type],...]
18
- captive-release new [patch|minor|major|x.y.z] [--yes]
19
- captive-release version
20
-
21
- #{@terminal.bold("Commands:")}
22
- init Detect version files and write captive-release.json
23
- new Bump version, commit, tag, push, create GitHub release
24
- Refuse de tourner sur un arbre de travail sale.
25
- Demande toujours confirmation, sauf avec --yes.
26
- build Dispatch an EAS build workflow (staging/production) via GitHub Actions
27
-
28
- #{@terminal.bold("Options for init:")}
29
- --files Non-interactive: comma-separated list of files with optional type
30
- Type is auto-detected (package_json or plain) when omitted
31
- Example: --files=package.json,ios/VERSION:plain
32
-
33
- #{@terminal.bold("Examples:")}
34
- captive-release init
35
- captive-release init --files=package.json,apps/mobile/package.json
36
- captive-release new
37
- captive-release new patch
38
- captive-release new 2.0.0
39
- captive-release new patch --yes
40
- captive-release build
41
- captive-release build --profile staging
42
- captive-release build --profile production
43
-
44
- #{@terminal.bold("GitHub release:")}
45
- Set GITHUB_TOKEN env var or authenticate with `gh auth login`
46
-
47
- HELP
17
+ @terminal.say "\n#{Messages.t(:help).gsub(TITRES) { |titre| @terminal.bold(titre) }}\n"
48
18
  end
49
19
  end
50
20
  end
@@ -22,13 +22,13 @@ module CaptiveRelease
22
22
 
23
23
  def avec_interaction
24
24
  @terminal.say "\n#{@terminal.bold("captive-release init")}\n"
25
- @terminal.say "Scanning for version files...\n"
25
+ @terminal.say "#{Messages.t(:scanning)}\n"
26
26
 
27
27
  trouves = VersionDetector.scan
28
- return creer_un_version("No version files found (package.json, VERSION).") if trouves.empty?
28
+ return creer_un_version(Messages.t(:no_version_files_found)) if trouves.empty?
29
29
 
30
30
  retenus = selection.call(trouves)
31
- return creer_un_version("Aucun fichier sélectionné.") if retenus.empty?
31
+ return creer_un_version(Messages.t(:no_files_selected)) if retenus.empty?
32
32
 
33
33
  writer.call(retenus)
34
34
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CaptiveRelease
4
+ # Dans quelle langue parler au développeur : celle qu'il force, sinon celle de son système
5
+ # sous macOS, sinon celle que son environnement déclare, sinon l'anglais.
6
+ #
7
+ # Cette unité change quand les sources de la langue changent, Messages quand les textes
8
+ # changent. La langue de macOS passe devant LC_ALL et LANG : un shell les hérite souvent en
9
+ # anglais sans que personne ne l'ait choisi, alors que la langue du système, elle, l'a été.
10
+ # CAPTIVE_RELEASE_LANG reste le moyen de forcer une langue sans toucher au reste.
11
+ module Locale
12
+ SUPPORTED = %w[en fr].freeze
13
+ DEFAULT = "en"
14
+ FORCEE = "CAPTIVE_RELEASE_LANG"
15
+ POSIX = %w[LC_ALL LC_MESSAGES LANG].freeze
16
+
17
+ def self.detect(env: ENV, system: method(:systeme))
18
+ candidates = [ env[FORCEE], system.call, *env.values_at(*POSIX) ]
19
+ langue = candidates.compact.reject(&:empty?).first.to_s[0, 2].downcase
20
+ SUPPORTED.include?(langue) ? langue : DEFAULT
21
+ end
22
+
23
+ # Hors macOS, rien : les variables POSIX décident.
24
+ def self.systeme(plateforme = RUBY_PLATFORM)
25
+ return "" unless plateforme.include?("darwin")
26
+
27
+ `defaults read -g AppleLocale 2>/dev/null`.strip
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,106 @@
1
+ en:
2
+ captive_release:
3
+ help: |
4
+ captive-release : Semver release tool
5
+
6
+ Usage:
7
+ captive-release init [--files path[:type],...]
8
+ captive-release new [patch|minor|major|x.y.z] [--yes]
9
+ captive-release build [--profile staging|production]
10
+ captive-release version
11
+
12
+ Commands:
13
+ init Detect version files and write captive-release.json
14
+ new Bump version, commit, tag, push, create GitHub release
15
+ Refuses to run on a dirty working tree.
16
+ Always asks for confirmation, unless --yes.
17
+ build Dispatch a mobile build workflow (staging/production) via GitHub Actions
18
+
19
+ Options for init:
20
+ --files Non-interactive: comma-separated list of files with optional type
21
+ Type is auto-detected (package_json or plain) when omitted
22
+ Example: --files=package.json,ios/VERSION:plain
23
+
24
+ Examples:
25
+ captive-release init
26
+ captive-release init --files=package.json,apps/mobile/package.json
27
+ captive-release new
28
+ captive-release new patch
29
+ captive-release new 2.0.0
30
+ captive-release new patch --yes
31
+ captive-release build
32
+ captive-release build --profile staging
33
+ captive-release build --profile production
34
+
35
+ GitHub release:
36
+ Set GITHUB_TOKEN env var or authenticate with `gh auth login`
37
+
38
+ Language:
39
+ Detected from CAPTIVE_RELEASE_LANG, the macOS language, then LC_ALL, LC_MESSAGES or LANG (en, fr)
40
+ 'yes': 'Yes'
41
+ 'no': 'No'
42
+ invalid_choice: Invalid choice.
43
+ unknown_command: 'Unknown command: %{command}'
44
+ scanning: Scanning for version files...
45
+ no_version_files_found: No version files found (package.json, VERSION).
46
+ no_files_selected: No files selected.
47
+ single_file_selected: '1 file detected: selected automatically.'
48
+ select_prompt: 'Press %{enter} to select all, or enter the numbers to %{exclude} (e.g. 2,3):'
49
+ select_enter: Enter
50
+ select_exclude: exclude
51
+ create_version_file: 'Create a %{file} file? [y/N] '
52
+ version_file_created: "%{file} created with %{version}"
53
+ config_written:
54
+ one: "✅ captive-release.json written with %{count} version file."
55
+ other: "✅ captive-release.json written with %{count} version files."
56
+ expo_workflow: 'Expo build workflow: %{workflow}'
57
+ capacitor_workflow: 'Capacitor build workflow: %{workflow}'
58
+ framework_detected: "%{framework} project detected."
59
+ configure_build: Configure build dispatch?
60
+ workflow_file_name: 'Workflow file name %{hint}:'
61
+ workflow_default: "(default: %{workflow})"
62
+ no_version_file: No version file found (package.json, lib/**/version.rb, VERSION).
63
+ create_version_or_init: Create a %{file} file or run %{command}.
64
+ config_not_found: "%{file} not found. Run `captive-release init` first."
65
+ version_files_empty: version_files cannot be empty
66
+ no_build_config: No build configuration in %{file}.
67
+ run_init_for_build: Run %{command} to configure the build workflow.
68
+ build_token_required: GITHUB_TOKEN or `gh auth login` required to dispatch build workflows.
69
+ choose_profile: 'Choose profile:'
70
+ dispatch_profile: 'Profile: %{value}'
71
+ dispatch_workflow: 'Workflow: %{value}'
72
+ dispatch_branch: 'Branch: %{value}'
73
+ dispatching: Dispatching...
74
+ build_dispatched: Build dispatched!
75
+ dirty_tree: The working tree is not clean.
76
+ dirty_tree_hint: Commit or stash your changes before releasing.
77
+ current_version: 'Current version: %{version}'
78
+ changes: 'Changes: %{link}'
79
+ suggestion: 'Suggestion: %{severity} (%{detail})'
80
+ suggestion_major: "\U0001F4A5 detected"
81
+ suggestion_minor: "✨ detected"
82
+ suggestion_patch: no notable commit detected
83
+ choose_release_type: 'Choose release type:'
84
+ custom: Custom
85
+ enter_version: 'Enter version (x.y.z): '
86
+ invalid_bump_type: 'Invalid bump type: %{type}. Use patch|minor|major|x.y.z'
87
+ invalid_semver: 'Invalid semver: %{value}'
88
+ invalid_version: 'Invalid version: %{value}'
89
+ bump_warning: "⚠️ Commits suggest a %{suggestion} bump, but the chosen version is only a %{chosen}."
90
+ continue_anyway: 'Continue anyway? [y/N] '
91
+ new_version: 'New version: %{version}'
92
+ confirm: 'Confirm? [Y/n] '
93
+ updating_version_files: Updating version files...
94
+ refreshing_lockfile: Refreshing Gemfile.lock...
95
+ running_git: Running git commands...
96
+ creating_github_release: Creating GitHub release...
97
+ no_token_skip_release: "⚠️ GITHUB_TOKEN not set - skipping GitHub release creation."
98
+ released: Version v%{version} released successfully!
99
+ unknown_type: 'Unknown type %{type}. Must be one of: %{types}'
100
+ cannot_read_version: 'Cannot read version from %{path} : expected pattern: %{expected}'
101
+ file_not_found: 'File not found: %{path}'
102
+ command_failed: 'Command failed: %{command}'
103
+ github_api_error: 'GitHub API error (%{code}): %{message}'
104
+ too_many_redirects: Too many redirects from GitHub API
105
+ redirect_without_location: GitHub redirect without Location header
106
+ cannot_parse_remote: 'Cannot parse GitHub repo from remote URL: %{url}'
@@ -0,0 +1,106 @@
1
+ fr:
2
+ captive_release:
3
+ help: |
4
+ captive-release : Outil de release semver
5
+
6
+ Utilisation :
7
+ captive-release init [--files path[:type],...]
8
+ captive-release new [patch|minor|major|x.y.z] [--yes]
9
+ captive-release build [--profile staging|production]
10
+ captive-release version
11
+
12
+ Commandes :
13
+ init Détecte les fichiers de version et écrit captive-release.json
14
+ new Bumpe la version, commite, tague, pousse, crée la release GitHub
15
+ Refuse de tourner sur un arbre de travail sale.
16
+ Demande toujours confirmation, sauf avec --yes.
17
+ build Déclenche un workflow de build mobile (staging/production) via GitHub Actions
18
+
19
+ Options de init :
20
+ --files Non interactif : liste de fichiers séparés par des virgules, type optionnel
21
+ Le type est déduit (package_json ou plain) s'il est omis
22
+ Exemple : --files=package.json,ios/VERSION:plain
23
+
24
+ Exemples :
25
+ captive-release init
26
+ captive-release init --files=package.json,apps/mobile/package.json
27
+ captive-release new
28
+ captive-release new patch
29
+ captive-release new 2.0.0
30
+ captive-release new patch --yes
31
+ captive-release build
32
+ captive-release build --profile staging
33
+ captive-release build --profile production
34
+
35
+ Release GitHub :
36
+ Définissez la variable GITHUB_TOKEN ou authentifiez-vous avec `gh auth login`
37
+
38
+ Langue :
39
+ Déduite de CAPTIVE_RELEASE_LANG, de la langue de macOS, puis de LC_ALL, LC_MESSAGES ou LANG (en, fr)
40
+ 'yes': Oui
41
+ 'no': Non
42
+ invalid_choice: Choix invalide.
43
+ unknown_command: 'Commande inconnue : %{command}'
44
+ scanning: Recherche des fichiers de version...
45
+ no_version_files_found: Aucun fichier de version trouvé (package.json, VERSION).
46
+ no_files_selected: Aucun fichier sélectionné.
47
+ single_file_selected: '1 fichier détecté : sélectionné automatiquement.'
48
+ select_prompt: 'Appuyez sur %{enter} pour tout sélectionner, ou entrez les numéros à %{exclude} (ex: 2,3) :'
49
+ select_enter: Entrée
50
+ select_exclude: exclure
51
+ create_version_file: 'Créer un fichier %{file} ? [o/N] '
52
+ version_file_created: "%{file} créé avec %{version}"
53
+ config_written:
54
+ one: "✅ captive-release.json écrit avec %{count} fichier de version."
55
+ other: "✅ captive-release.json écrit avec %{count} fichiers de version."
56
+ expo_workflow: 'Workflow de build Expo : %{workflow}'
57
+ capacitor_workflow: 'Workflow de build Capacitor : %{workflow}'
58
+ framework_detected: Projet %{framework} détecté.
59
+ configure_build: Configurer le déclenchement du build ?
60
+ workflow_file_name: 'Nom du fichier de workflow %{hint} :'
61
+ workflow_default: "(par défaut : %{workflow})"
62
+ no_version_file: Aucun fichier de version trouvé (package.json, lib/**/version.rb, VERSION).
63
+ create_version_or_init: Créez un fichier %{file} ou lancez %{command}.
64
+ config_not_found: "%{file} introuvable. Lancez d'abord `captive-release init`."
65
+ version_files_empty: version_files ne peut pas être vide
66
+ no_build_config: Aucune configuration de build dans %{file}.
67
+ run_init_for_build: Lancez %{command} pour configurer le workflow de build.
68
+ build_token_required: GITHUB_TOKEN ou `gh auth login` requis pour déclencher les workflows de build.
69
+ choose_profile: 'Choisissez le profil :'
70
+ dispatch_profile: 'Profil : %{value}'
71
+ dispatch_workflow: 'Workflow : %{value}'
72
+ dispatch_branch: 'Branche : %{value}'
73
+ dispatching: Déclenchement...
74
+ build_dispatched: Build déclenché !
75
+ dirty_tree: L'arbre de travail n'est pas propre.
76
+ dirty_tree_hint: Commitez ou remisez vos modifications avant de releaser.
77
+ current_version: 'Version actuelle : %{version}'
78
+ changes: 'Changements : %{link}'
79
+ suggestion: 'Suggestion : %{severity} (%{detail})'
80
+ suggestion_major: "\U0001F4A5 détecté"
81
+ suggestion_minor: "✨ détecté"
82
+ suggestion_patch: aucun commit notable détecté
83
+ choose_release_type: 'Choisissez le type de release :'
84
+ custom: Personnalisée
85
+ enter_version: 'Saisissez la version (x.y.z) : '
86
+ invalid_bump_type: 'Type de bump invalide : %{type}. Utilisez patch|minor|major|x.y.z'
87
+ invalid_semver: 'Semver invalide : %{value}'
88
+ invalid_version: 'Version invalide : %{value}'
89
+ bump_warning: "⚠️ Les commits suggèrent un bump %{suggestion}, mais la version choisie n'est qu'un %{chosen}."
90
+ continue_anyway: 'Continuer quand même ? [o/N] '
91
+ new_version: 'Nouvelle version : %{version}'
92
+ confirm: 'Confirmer ? [O/n] '
93
+ updating_version_files: Mise à jour des fichiers de version...
94
+ refreshing_lockfile: Rafraîchissement du Gemfile.lock...
95
+ running_git: Exécution des commandes git...
96
+ creating_github_release: Création de la release GitHub...
97
+ no_token_skip_release: "⚠️ GITHUB_TOKEN absent - la release GitHub n'est pas créée."
98
+ released: Version v%{version} publiée avec succès !
99
+ unknown_type: 'Type inconnu %{type}. Valeurs possibles : %{types}'
100
+ cannot_read_version: 'Impossible de lire la version dans %{path} : motif attendu : %{expected}'
101
+ file_not_found: 'Fichier introuvable : %{path}'
102
+ command_failed: 'Échec de la commande : %{command}'
103
+ github_api_error: 'Erreur de l''API GitHub (%{code}) : %{message}'
104
+ too_many_redirects: Trop de redirections de l'API GitHub
105
+ redirect_without_location: Redirection GitHub sans en-tête Location
106
+ cannot_parse_remote: 'Impossible de déduire le dépôt GitHub du remote : %{url}'
@@ -26,7 +26,7 @@ module CaptiveRelease
26
26
  private
27
27
 
28
28
  def refuse
29
- @terminal.say @terminal.error("Invalid choice.")
29
+ @terminal.say @terminal.error(Messages.t(:invalid_choice))
30
30
  exit 1
31
31
  end
32
32
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "i18n"
4
+ require_relative "locale"
5
+
6
+ module CaptiveRelease
7
+ # Les textes de la CLI dans la langue du développeur, lus par I18n depuis locales/*.yml.
8
+ #
9
+ # Cette unité change quand la façon de retrouver un texte change, les fichiers YAML quand un
10
+ # texte change, Locale quand la détection de la langue change. Tout passe sous la portée
11
+ # captive_release, pour ne pas marcher sur les traductions d'un hôte qui chargerait la gem.
12
+ module Messages
13
+ OUI = %w[y yes o oui].freeze
14
+
15
+ I18n.load_path |= Dir["#{__dir__}/locales/*.yml"]
16
+
17
+ def self.locale = @locale ||= Locale.detect
18
+
19
+ def self.locale=(langue)
20
+ @locale = langue
21
+ end
22
+
23
+ # raise : un texte manquant doit casser la suite de tests, pas s'afficher en
24
+ # « Translation missing » devant le développeur.
25
+ def self.t(cle, **valeurs) = I18n.t(cle, scope: :captive_release, locale: locale, raise: true, **valeurs)
26
+
27
+ # « o » comme « y » : l'invite française propose [o/N], mais un réflexe anglais ne doit
28
+ # pas se transformer en refus.
29
+ def self.yes?(reponse) = OUI.include?(reponse.strip.downcase)
30
+ end
31
+ end
@@ -15,22 +15,15 @@ module CaptiveRelease
15
15
  def run(bump_arg, confirmation:)
16
16
  ReleasePreconditions.new(terminal: @terminal).verifier!
17
17
  courante = @config.current_version
18
- @terminal.say "\n#{@terminal.bold("captive-release")}\n"
19
- @terminal.say "Current version: #{@terminal.bold(courante)}\n"
20
- afficher_changements(courante)
21
- suggestion = BumpSuggestion.for(courante)
22
- afficher_suggestion(suggestion)
18
+ suggestion = ReleaseOverview.new(terminal: @terminal).call(courante)
23
19
 
24
20
  prochaine = VersionChoice.new(terminal: @terminal).call(courante, bump_arg)
25
- if confirmation
26
- BumpSafetyNet.new(terminal: @terminal).verifier!(suggestion, courante, prochaine)
27
- confirmer!(prochaine)
28
- end
21
+ ReleaseConfirmation.new(terminal: @terminal).verifier!(suggestion, courante, prochaine) if confirmation
29
22
 
30
23
  VersionFilesWriting.new(terminal: @terminal).call(prochaine, @config.version_files)
31
24
  GitRelease.new(terminal: @terminal).call(prochaine, chemins_a_commiter)
32
25
  publication.call(prochaine)
33
- @terminal.say "\n🎉 #{@terminal.bold("Version v#{prochaine} released successfully!")}\n"
26
+ @terminal.say "\n🎉 #{@terminal.bold(Messages.t(:released, version: prochaine))}\n"
34
27
  end
35
28
 
36
29
  private
@@ -39,33 +32,13 @@ module CaptiveRelease
39
32
  chemins = @config.version_files.map(&:path)
40
33
  return chemins unless (refresh = GemfileLockRefresh.new).needed?
41
34
 
42
- @terminal.say "\n#{@terminal.bold("Refreshing Gemfile.lock...")}"
35
+ @terminal.say "\n#{@terminal.bold(Messages.t(:refreshing_lockfile))}"
43
36
  refresh.call
44
37
  @terminal.done GemfileLockRefresh::LOCKFILE
45
38
 
46
39
  chemins + [ GemfileLockRefresh::LOCKFILE ]
47
40
  end
48
41
 
49
- def afficher_changements(courante)
50
- lien = ChangesSince.url(courante)
51
- return unless lien
52
-
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"
61
- end
62
-
63
- def confirmer!(version)
64
- @terminal.say "\nNew version: #{@terminal.bold(version)}"
65
- reponse = @terminal.ask("Confirm? [Y/n] ").downcase
66
- exit 1 unless reponse == "y" || reponse.empty?
67
- end
68
-
69
42
  def publication
70
43
  ReleasePublication.new(terminal: @terminal, token_provider: @token_provider)
71
44
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CaptiveRelease
4
+ # Les points d'arrêt avant ce qui ne se défait pas : le garde-fou quand la version est en
5
+ # deçà de ce que les commits suggèrent, puis la confirmation de la nouvelle version.
6
+ #
7
+ # Cette unité change quand la politique de confirmation change, ReleaseCommand quand la
8
+ # suite des étapes change.
9
+ class ReleaseConfirmation
10
+ def initialize(terminal:)
11
+ @terminal = terminal
12
+ end
13
+
14
+ def verifier!(suggestion, courante, prochaine)
15
+ BumpSafetyNet.new(terminal: @terminal).verifier!(suggestion, courante, prochaine)
16
+ @terminal.say "\n#{Messages.t(:new_version, version: @terminal.bold(prochaine))}"
17
+ reponse = @terminal.ask(Messages.t(:confirm))
18
+ exit 1 unless reponse.strip.empty? || Messages.yes?(reponse)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CaptiveRelease
4
+ # Ce qu'on montre avant de choisir la version : d'où l'on part, ce qui a changé depuis, et
5
+ # le bump que les commits suggèrent. La suggestion est rendue, le garde-fou en a besoin.
6
+ #
7
+ # Cette unité change quand ce qu'on montre avant le choix change, ReleaseCommand quand la
8
+ # suite des étapes change.
9
+ class ReleaseOverview
10
+ def initialize(terminal:)
11
+ @terminal = terminal
12
+ end
13
+
14
+ def call(courante)
15
+ @terminal.say "\n#{@terminal.bold("captive-release")}\n"
16
+ @terminal.say Messages.t(:current_version, version: @terminal.bold(courante))
17
+ afficher_changements(courante)
18
+ BumpSuggestion.for(courante).tap do |suggestion|
19
+ afficher_suggestion(suggestion)
20
+ @terminal.say
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def afficher_changements(courante)
27
+ lien = ChangesSince.url(courante)
28
+ return unless lien
29
+
30
+ @terminal.say "\n#{Messages.t(:changes, link: @terminal.link(lien))}"
31
+ end
32
+
33
+ def afficher_suggestion(suggestion)
34
+ return unless suggestion
35
+
36
+ detail = Messages.t(:"suggestion_#{suggestion}")
37
+ @terminal.say Messages.t(:suggestion, severity: suggestion, detail: detail)
38
+ end
39
+ end
40
+ end
@@ -16,8 +16,8 @@ module CaptiveRelease
16
16
  def verifier!
17
17
  return unless GitCommands.dirty?
18
18
 
19
- @terminal.say @terminal.error("L'arbre de travail n'est pas propre.")
20
- @terminal.say "Commitez ou remisez vos modifications avant de releaser.\n"
19
+ @terminal.say @terminal.error(Messages.t(:dirty_tree))
20
+ @terminal.say "#{Messages.t(:dirty_tree_hint)}\n"
21
21
  exit 1
22
22
  end
23
23
  end
@@ -13,7 +13,7 @@ module CaptiveRelease
13
13
  end
14
14
 
15
15
  def call(version)
16
- @terminal.say "\n#{@terminal.bold("Creating GitHub release...")}"
16
+ @terminal.say "\n#{@terminal.bold(Messages.t(:creating_github_release))}"
17
17
  jeton = @token_provider.call
18
18
  return sans_jeton unless jeton
19
19
 
@@ -24,7 +24,7 @@ module CaptiveRelease
24
24
  private
25
25
 
26
26
  def sans_jeton
27
- @terminal.say " #{@terminal.warn("⚠️ GITHUB_TOKEN not set - skipping GitHub release creation.")}"
27
+ @terminal.say " #{@terminal.warn(Messages.t(:no_token_skip_release))}"
28
28
  end
29
29
  end
30
30
  end
@@ -8,7 +8,7 @@ module CaptiveRelease
8
8
  # commandes d'une release changent.
9
9
  module Shell
10
10
  def self.run!(commande)
11
- system(commande) || raise("Command failed: #{commande}")
11
+ system(commande) || raise(Messages.t(:command_failed, command: commande))
12
12
  end
13
13
 
14
14
  def self.capture(commande) = `#{commande}`.strip
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CaptiveRelease
4
- VERSION = "0.7.1"
4
+ VERSION = "0.7.2"
5
5
  end
@@ -9,7 +9,7 @@ module CaptiveRelease
9
9
  end
10
10
 
11
11
  def initialize(current)
12
- raise ArgumentError, "Invalid semver: #{current}" unless current.match?(SEMVER_RE)
12
+ raise ArgumentError, Messages.t(:invalid_semver, value: current) unless current.match?(SEMVER_RE)
13
13
 
14
14
  @major, @minor, @patch = current.split(".").map(&:to_i)
15
15
  end
@@ -20,7 +20,7 @@ module CaptiveRelease
20
20
  when "minor" then "#{@major}.#{@minor + 1}.0"
21
21
  when "patch" then "#{@major}.#{@minor}.#{@patch + 1}"
22
22
  else
23
- raise ArgumentError, "Invalid version: #{type}" unless type.to_s.match?(SEMVER_RE)
23
+ raise ArgumentError, Messages.t(:invalid_version, value: type) unless type.to_s.match?(SEMVER_RE)
24
24
 
25
25
  type.to_s
26
26
  end
@@ -24,11 +24,11 @@ module CaptiveRelease
24
24
  def resolve(current, type)
25
25
  return VersionBumper.bump(current, type) if type.match?(SEMVER_RE) || BUMP_TYPES.include?(type)
26
26
 
27
- refuse("Invalid bump type: #{type}. Use patch|minor|major|x.y.z")
27
+ refuse(Messages.t(:invalid_bump_type, type: type))
28
28
  end
29
29
 
30
30
  def ask(current)
31
- choisi = Menu.new(terminal: @terminal).choose("Choose release type:", candidates_for(current))
31
+ choisi = Menu.new(terminal: @terminal).choose(Messages.t(:choose_release_type), candidates_for(current))
32
32
  choisi == CUSTOM ? type_it : choisi
33
33
  end
34
34
 
@@ -38,14 +38,14 @@ module CaptiveRelease
38
38
  version = VersionBumper.bump(current, type)
39
39
  [ "#{type.capitalize} → #{@terminal.bold(version)}", version ]
40
40
  end
41
- candidats.merge("#{CUSTOM} → ?" => CUSTOM)
41
+ candidats.merge("#{Messages.t(:custom)} → ?" => CUSTOM)
42
42
  end
43
43
 
44
44
  def type_it
45
- version = @terminal.ask("Enter version (x.y.z): ")
45
+ version = @terminal.ask(Messages.t(:enter_version))
46
46
  return version if version.match?(SEMVER_RE)
47
47
 
48
- refuse("Invalid semver: #{version}")
48
+ refuse(Messages.t(:invalid_semver, value: version))
49
49
  end
50
50
 
51
51
  def refuse(message)
@@ -10,7 +10,7 @@ module CaptiveRelease
10
10
  attr_reader :path, :type
11
11
 
12
12
  def initialize(path, type)
13
- raise ArgumentError, "Unknown type #{type}. Must be one of: #{TYPES.join(", ")}" unless TYPES.include?(type)
13
+ raise ArgumentError, Messages.t(:unknown_type, type: type, types: TYPES.join(", ")) unless TYPES.include?(type)
14
14
 
15
15
  @path = path
16
16
  @type = type
@@ -19,7 +19,7 @@ module CaptiveRelease
19
19
 
20
20
  def read_version
21
21
  @format.read(lire!) ||
22
- raise("Cannot read version from #{path} : expected pattern: #{@format.expected}")
22
+ raise(Messages.t(:cannot_read_version, path: path, expected: @format.expected))
23
23
  end
24
24
 
25
25
  def write_version(new_version)
@@ -34,7 +34,7 @@ module CaptiveRelease
34
34
  private
35
35
 
36
36
  def lire!
37
- raise "File not found: #{path}" unless File.exist?(path)
37
+ raise Messages.t(:file_not_found, path: path) unless File.exist?(path)
38
38
 
39
39
  File.read(path)
40
40
  end
@@ -14,11 +14,11 @@ module CaptiveRelease
14
14
 
15
15
  def call(motif)
16
16
  @terminal.say @terminal.warn(motif)
17
- @terminal.say "\nCreate a #{@terminal.bold("VERSION")} file? [y/N] "
18
- exit 1 unless @terminal.ask.downcase == "y"
17
+ @terminal.say "\n#{Messages.t(:create_version_file, file: @terminal.bold("VERSION"))}"
18
+ exit 1 unless Messages.yes?(@terminal.ask)
19
19
 
20
20
  File.write("VERSION", "#{VERSION_INITIALE}\n")
21
- @terminal.done "VERSION créé avec #{@terminal.bold(VERSION_INITIALE)}"
21
+ @terminal.done Messages.t(:version_file_created, file: "VERSION", version: @terminal.bold(VERSION_INITIALE))
22
22
  [ VersionFile.new("VERSION", "plain") ]
23
23
  end
24
24
  end
@@ -26,14 +26,14 @@ module CaptiveRelease
26
26
  end
27
27
 
28
28
  def demander
29
- @terminal.say "\nAppuyez sur #{@terminal.bold("Entrée")} pour tout sélectionner, " \
30
- "ou entrez les numéros à #{@terminal.bold("exclure")} (ex: 2,3) :"
29
+ @terminal.say "\n#{Messages.t(:select_prompt, enter: @terminal.bold(Messages.t(:select_enter)),
30
+ exclude: @terminal.bold(Messages.t(:select_exclude)))}"
31
31
  @terminal.ask
32
32
  end
33
33
 
34
34
  def seul(found)
35
35
  @terminal.say ""
36
- @terminal.done "1 fichier détecté : sélectionné automatiquement."
36
+ @terminal.done Messages.t(:single_file_selected)
37
37
  found
38
38
  end
39
39
 
@@ -12,7 +12,7 @@ module CaptiveRelease
12
12
  end
13
13
 
14
14
  def call(version, fichiers)
15
- @terminal.say "\n#{@terminal.bold("Updating version files...")}"
15
+ @terminal.say "\n#{@terminal.bold(Messages.t(:updating_version_files))}"
16
16
  fichiers.each do |fichier|
17
17
  fichier.write_version(version)
18
18
  @terminal.done fichier.path
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "captive_release/version"
4
+ require_relative "captive_release/messages"
4
5
  require_relative "captive_release/version_bumper"
5
6
  require_relative "captive_release/version_format"
6
7
  require_relative "captive_release/version_file"
@@ -42,6 +43,8 @@ require_relative "captive_release/changes_since"
42
43
  require_relative "captive_release/release_publication"
43
44
  require_relative "captive_release/version_files_writing"
44
45
  require_relative "captive_release/release_preconditions"
46
+ require_relative "captive_release/release_overview"
47
+ require_relative "captive_release/release_confirmation"
45
48
  require_relative "captive_release/release_command"
46
49
  require_relative "captive_release/help"
47
50
  require_relative "captive_release/cli"
metadata CHANGED
@@ -1,14 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: captive-release
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Captive Studio
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies: []
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: i18n
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - '='
17
+ - !ruby/object:Gem::Version
18
+ version: 1.15.2
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - '='
24
+ - !ruby/object:Gem::Version
25
+ version: 1.15.2
12
26
  description: Gère les bumps de version (patch/minor/major/custom), met à jour package.json
13
27
  et fichiers VERSION, crée le commit, le tag et la release GitHub.
14
28
  email:
@@ -39,6 +53,7 @@ files:
39
53
  - lib/captive_release/git_release.rb
40
54
  - lib/captive_release/github_api.rb
41
55
  - lib/captive_release/github_dispatch.rb
56
+ - lib/captive_release/github_error_message.rb
42
57
  - lib/captive_release/github_http.rb
43
58
  - lib/captive_release/github_release.rb
44
59
  - lib/captive_release/github_repo.rb
@@ -46,9 +61,15 @@ files:
46
61
  - lib/captive_release/gitmoji_severity.rb
47
62
  - lib/captive_release/help.rb
48
63
  - lib/captive_release/init_command.rb
64
+ - lib/captive_release/locale.rb
65
+ - lib/captive_release/locales/en.yml
66
+ - lib/captive_release/locales/fr.yml
49
67
  - lib/captive_release/menu.rb
68
+ - lib/captive_release/messages.rb
50
69
  - lib/captive_release/options.rb
51
70
  - lib/captive_release/release_command.rb
71
+ - lib/captive_release/release_confirmation.rb
72
+ - lib/captive_release/release_overview.rb
52
73
  - lib/captive_release/release_preconditions.rb
53
74
  - lib/captive_release/release_publication.rb
54
75
  - lib/captive_release/repo_files.rb