mnenv 0.1.0 → 0.1.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.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/PROPOSAL.md +197 -0
- data/README.adoc +168 -461
- data/Rakefile +7 -4
- data/bin/Install-Mnenv.ps1 +145 -0
- data/bin/mnenv-installer +72 -0
- data/completions/bash +47 -0
- data/completions/fish +29 -0
- data/completions/powershell.ps1 +94 -0
- data/completions/zsh +43 -0
- data/lib/mnenv/binary_repository.rb +189 -0
- data/lib/mnenv/chocolatey.rb +7 -0
- data/lib/mnenv/cli.rb +110 -10
- data/lib/mnenv/commands/available_command.rb +169 -0
- data/lib/mnenv/commands/chocolatey_command.rb +4 -5
- data/lib/mnenv/commands/gemfile_command.rb +4 -5
- data/lib/mnenv/commands/homebrew_command.rb +4 -5
- data/lib/mnenv/commands/install_command.rb +234 -0
- data/lib/mnenv/commands/snap_command.rb +5 -7
- data/lib/mnenv/commands/uninstall_command.rb +111 -0
- data/lib/mnenv/commands/version_command.rb +167 -0
- data/lib/mnenv/commands.rb +9 -4
- data/lib/mnenv/gemfile/extractor.rb +10 -3
- data/lib/mnenv/gemfile.rb +8 -0
- data/lib/mnenv/gemfile_repository.rb +0 -2
- data/lib/mnenv/homebrew.rb +7 -0
- data/lib/mnenv/installer/base.rb +62 -0
- data/lib/mnenv/installer/factory.rb +46 -0
- data/lib/mnenv/installer.rb +12 -0
- data/lib/mnenv/installers/binary_installer.rb +242 -0
- data/lib/mnenv/installers/gemfile_installer.rb +76 -0
- data/lib/mnenv/json_formatter.rb +3 -13
- data/lib/mnenv/logger.rb +9 -1
- data/lib/mnenv/models/binary_version.rb +78 -0
- data/lib/mnenv/models/chocolatey_version.rb +7 -0
- data/lib/mnenv/models/gemfile_version.rb +19 -5
- data/lib/mnenv/models/homebrew_version.rb +7 -0
- data/lib/mnenv/models/snap_version.rb +8 -0
- data/lib/mnenv/models/version.rb +16 -0
- data/lib/mnenv/models.rb +7 -5
- data/lib/mnenv/paths.rb +69 -0
- data/lib/mnenv/platform_detector.rb +109 -0
- data/lib/mnenv/repository.rb +50 -35
- data/lib/mnenv/resolver +72 -0
- data/lib/mnenv/shells/base.rb +32 -0
- data/lib/mnenv/shells/bash.rb +72 -0
- data/lib/mnenv/shells/cmd.rb +108 -0
- data/lib/mnenv/shells/factory.rb +82 -0
- data/lib/mnenv/shells/power_shell.rb +110 -0
- data/lib/mnenv/shim_manager.rb +121 -0
- data/lib/mnenv/snap.rb +7 -0
- data/lib/mnenv/snap_repository.rb +2 -19
- data/lib/mnenv/source_registry.rb +69 -0
- data/lib/mnenv/sources.rb +46 -0
- data/lib/mnenv/version.rb +1 -1
- data/lib/mnenv/version_resolver.rb +108 -0
- data/lib/mnenv/versions_manager.rb +92 -0
- data/lib/mnenv.rb +6 -0
- data/mnenv.gemspec +4 -1
- data/scripts/cross-source-switching-test.sh +214 -0
- data/scripts/integration-test.sh +89 -0
- data/scripts/version-switching-test.sh +151 -0
- metadata +85 -247
- data/data/chocolatey/versions.yaml +0 -812
- data/data/gemfile/v1.1.6/Gemfile +0 -4
- data/data/gemfile/v1.1.6/Gemfile.lock.archived +0 -232
- data/data/gemfile/v1.1.7/Gemfile +0 -4
- data/data/gemfile/v1.1.7/Gemfile.lock.archived +0 -235
- data/data/gemfile/v1.1.8/Gemfile +0 -4
- data/data/gemfile/v1.1.8/Gemfile.lock.archived +0 -238
- data/data/gemfile/v1.10.0/Gemfile +0 -5
- data/data/gemfile/v1.10.0/Gemfile.lock.archived +0 -930
- data/data/gemfile/v1.10.1/Gemfile +0 -5
- data/data/gemfile/v1.10.1/Gemfile.lock.archived +0 -929
- data/data/gemfile/v1.10.10/Gemfile +0 -5
- data/data/gemfile/v1.10.10/Gemfile.lock.archived +0 -973
- data/data/gemfile/v1.10.11/Gemfile +0 -5
- data/data/gemfile/v1.10.11/Gemfile.lock.archived +0 -975
- data/data/gemfile/v1.10.2/Gemfile +0 -5
- data/data/gemfile/v1.10.2/Gemfile.lock.archived +0 -939
- data/data/gemfile/v1.10.3/Gemfile +0 -5
- data/data/gemfile/v1.10.3/Gemfile.lock.archived +0 -946
- data/data/gemfile/v1.10.5/Gemfile +0 -5
- data/data/gemfile/v1.10.5/Gemfile.lock.archived +0 -958
- data/data/gemfile/v1.10.6/Gemfile +0 -5
- data/data/gemfile/v1.10.6/Gemfile.lock.archived +0 -969
- data/data/gemfile/v1.10.7/Gemfile +0 -5
- data/data/gemfile/v1.10.7/Gemfile.lock.archived +0 -969
- data/data/gemfile/v1.10.8/Gemfile +0 -5
- data/data/gemfile/v1.10.8/Gemfile.lock.archived +0 -968
- data/data/gemfile/v1.10.9/Gemfile +0 -5
- data/data/gemfile/v1.10.9/Gemfile.lock.archived +0 -972
- data/data/gemfile/v1.11.0/Gemfile +0 -5
- data/data/gemfile/v1.11.0/Gemfile.lock.archived +0 -971
- data/data/gemfile/v1.11.1/Gemfile +0 -5
- data/data/gemfile/v1.11.1/Gemfile.lock.archived +0 -975
- data/data/gemfile/v1.11.4/Gemfile +0 -5
- data/data/gemfile/v1.11.4/Gemfile.lock.archived +0 -1046
- data/data/gemfile/v1.11.5/Gemfile +0 -5
- data/data/gemfile/v1.11.5/Gemfile.lock.archived +0 -1047
- data/data/gemfile/v1.12.10/Gemfile +0 -3
- data/data/gemfile/v1.12.10/Gemfile.lock.archived +0 -1073
- data/data/gemfile/v1.12.3/Gemfile +0 -3
- data/data/gemfile/v1.12.3/Gemfile.lock.archived +0 -1050
- data/data/gemfile/v1.12.4/Gemfile +0 -3
- data/data/gemfile/v1.12.4/Gemfile.lock.archived +0 -1056
- data/data/gemfile/v1.12.5/Gemfile +0 -3
- data/data/gemfile/v1.12.5/Gemfile.lock.archived +0 -1054
- data/data/gemfile/v1.12.6/Gemfile +0 -3
- data/data/gemfile/v1.12.6/Gemfile.lock.archived +0 -1056
- data/data/gemfile/v1.12.8/Gemfile +0 -3
- data/data/gemfile/v1.12.8/Gemfile.lock.archived +0 -1063
- data/data/gemfile/v1.13.0/Gemfile +0 -3
- data/data/gemfile/v1.13.0/Gemfile.lock.archived +0 -1074
- data/data/gemfile/v1.13.2/Gemfile +0 -3
- data/data/gemfile/v1.13.2/Gemfile.lock.archived +0 -899
- data/data/gemfile/v1.13.3/Gemfile +0 -3
- data/data/gemfile/v1.13.3/Gemfile.lock.archived +0 -938
- data/data/gemfile/v1.13.4/Gemfile +0 -3
- data/data/gemfile/v1.13.4/Gemfile.lock.archived +0 -938
- data/data/gemfile/v1.13.5/Gemfile +0 -3
- data/data/gemfile/v1.13.5/Gemfile.lock.archived +0 -944
- data/data/gemfile/v1.13.7/Gemfile +0 -3
- data/data/gemfile/v1.13.7/Gemfile.lock.archived +0 -944
- data/data/gemfile/v1.13.8/Gemfile +0 -3
- data/data/gemfile/v1.13.8/Gemfile.lock.archived +0 -944
- data/data/gemfile/v1.13.9/Gemfile +0 -3
- data/data/gemfile/v1.13.9/Gemfile.lock.archived +0 -956
- data/data/gemfile/v1.14.3/Gemfile +0 -3
- data/data/gemfile/v1.14.3/Gemfile.lock.archived +0 -950
- data/data/gemfile/v1.2.12/Gemfile +0 -3
- data/data/gemfile/v1.2.12/Gemfile.lock.archived +0 -283
- data/data/gemfile/v1.2.2/Gemfile +0 -4
- data/data/gemfile/v1.2.2/Gemfile.lock.archived +0 -224
- data/data/gemfile/v1.2.3/Gemfile +0 -4
- data/data/gemfile/v1.2.3/Gemfile.lock.archived +0 -231
- data/data/gemfile/v1.2.6/Gemfile +0 -4
- data/data/gemfile/v1.2.6/Gemfile.lock.archived +0 -239
- data/data/gemfile/v1.2.8/Gemfile +0 -4
- data/data/gemfile/v1.2.8/Gemfile.lock.archived +0 -233
- data/data/gemfile/v1.2.9/Gemfile +0 -4
- data/data/gemfile/v1.2.9/Gemfile.lock.archived +0 -245
- data/data/gemfile/v1.3.1/Gemfile +0 -3
- data/data/gemfile/v1.3.1/Gemfile.lock.archived +0 -296
- data/data/gemfile/v1.3.2/Gemfile +0 -3
- data/data/gemfile/v1.3.2/Gemfile.lock.archived +0 -296
- data/data/gemfile/v1.3.4/Gemfile +0 -3
- data/data/gemfile/v1.3.4/Gemfile.lock.archived +0 -284
- data/data/gemfile/v1.3.5/Gemfile +0 -3
- data/data/gemfile/v1.3.5/Gemfile.lock.archived +0 -284
- data/data/gemfile/v1.3.6/Gemfile +0 -3
- data/data/gemfile/v1.3.6/Gemfile.lock.archived +0 -286
- data/data/gemfile/v1.3.9/Gemfile +0 -3
- data/data/gemfile/v1.3.9/Gemfile.lock.archived +0 -334
- data/data/gemfile/v1.4.0/Gemfile +0 -3
- data/data/gemfile/v1.4.0/Gemfile.lock.archived +0 -330
- data/data/gemfile/v1.4.10/Gemfile +0 -4
- data/data/gemfile/v1.4.10/Gemfile.lock.archived +0 -461
- data/data/gemfile/v1.4.11/Gemfile +0 -4
- data/data/gemfile/v1.4.11/Gemfile.lock.archived +0 -452
- data/data/gemfile/v1.4.12/Gemfile +0 -4
- data/data/gemfile/v1.4.12/Gemfile.lock.archived +0 -452
- data/data/gemfile/v1.4.13/Gemfile +0 -4
- data/data/gemfile/v1.4.13/Gemfile.lock.archived +0 -455
- data/data/gemfile/v1.4.14/Gemfile +0 -4
- data/data/gemfile/v1.4.14/Gemfile.lock.archived +0 -456
- data/data/gemfile/v1.4.18/Gemfile +0 -3
- data/data/gemfile/v1.4.18/Gemfile.lock.archived +0 -486
- data/data/gemfile/v1.4.3/Gemfile +0 -3
- data/data/gemfile/v1.4.3/Gemfile.lock.archived +0 -339
- data/data/gemfile/v1.4.4/Gemfile +0 -3
- data/data/gemfile/v1.4.4/Gemfile.lock.archived +0 -339
- data/data/gemfile/v1.4.5/Gemfile +0 -3
- data/data/gemfile/v1.4.5/Gemfile.lock.archived +0 -348
- data/data/gemfile/v1.4.6/Gemfile +0 -3
- data/data/gemfile/v1.4.6/Gemfile.lock.archived +0 -357
- data/data/gemfile/v1.4.7/Gemfile +0 -3
- data/data/gemfile/v1.4.7/Gemfile.lock.archived +0 -391
- data/data/gemfile/v1.4.8/Gemfile +0 -3
- data/data/gemfile/v1.4.8/Gemfile.lock.archived +0 -445
- data/data/gemfile/v1.4.9/Gemfile +0 -3
- data/data/gemfile/v1.4.9/Gemfile.lock.archived +0 -448
- data/data/gemfile/v1.5.0/Gemfile +0 -3
- data/data/gemfile/v1.5.0/Gemfile.lock.archived +0 -478
- data/data/gemfile/v1.5.10/Gemfile +0 -3
- data/data/gemfile/v1.5.10/Gemfile.lock.archived +0 -668
- data/data/gemfile/v1.5.11/Gemfile +0 -3
- data/data/gemfile/v1.5.11/Gemfile.lock.archived +0 -668
- data/data/gemfile/v1.5.15/Gemfile +0 -3
- data/data/gemfile/v1.5.15/Gemfile.lock.archived +0 -686
- data/data/gemfile/v1.5.16/Gemfile +0 -3
- data/data/gemfile/v1.5.16/Gemfile.lock.archived +0 -684
- data/data/gemfile/v1.5.17/Gemfile +0 -3
- data/data/gemfile/v1.5.17/Gemfile.lock.archived +0 -684
- data/data/gemfile/v1.5.18/Gemfile +0 -5
- data/data/gemfile/v1.5.18/Gemfile.lock.archived +0 -691
- data/data/gemfile/v1.5.19/Gemfile +0 -5
- data/data/gemfile/v1.5.19/Gemfile.lock.archived +0 -703
- data/data/gemfile/v1.5.20/Gemfile +0 -5
- data/data/gemfile/v1.5.20/Gemfile.lock.archived +0 -703
- data/data/gemfile/v1.5.21/Gemfile +0 -5
- data/data/gemfile/v1.5.21/Gemfile.lock.archived +0 -707
- data/data/gemfile/v1.5.22/Gemfile +0 -5
- data/data/gemfile/v1.5.22/Gemfile.lock.archived +0 -707
- data/data/gemfile/v1.5.23/Gemfile +0 -5
- data/data/gemfile/v1.5.23/Gemfile.lock.archived +0 -711
- data/data/gemfile/v1.5.24/Gemfile +0 -5
- data/data/gemfile/v1.5.24/Gemfile.lock.archived +0 -711
- data/data/gemfile/v1.5.3/Gemfile +0 -3
- data/data/gemfile/v1.5.3/Gemfile.lock.archived +0 -651
- data/data/gemfile/v1.5.4/Gemfile +0 -3
- data/data/gemfile/v1.5.4/Gemfile.lock.archived +0 -657
- data/data/gemfile/v1.5.5/Gemfile +0 -3
- data/data/gemfile/v1.5.5/Gemfile.lock.archived +0 -657
- data/data/gemfile/v1.5.6/Gemfile +0 -3
- data/data/gemfile/v1.5.6/Gemfile.lock.archived +0 -657
- data/data/gemfile/v1.5.7/Gemfile +0 -3
- data/data/gemfile/v1.5.7/Gemfile.lock.archived +0 -657
- data/data/gemfile/v1.5.8/Gemfile +0 -3
- data/data/gemfile/v1.5.8/Gemfile.lock.archived +0 -655
- data/data/gemfile/v1.5.9/Gemfile +0 -3
- data/data/gemfile/v1.5.9/Gemfile.lock.archived +0 -656
- data/data/gemfile/v1.6.1/Gemfile +0 -5
- data/data/gemfile/v1.6.1/Gemfile.lock.archived +0 -721
- data/data/gemfile/v1.6.10/Gemfile +0 -5
- data/data/gemfile/v1.6.10/Gemfile.lock.archived +0 -744
- data/data/gemfile/v1.6.11/Gemfile +0 -5
- data/data/gemfile/v1.6.11/Gemfile.lock.archived +0 -744
- data/data/gemfile/v1.6.12/Gemfile +0 -5
- data/data/gemfile/v1.6.12/Gemfile.lock.archived +0 -745
- data/data/gemfile/v1.6.13/Gemfile +0 -5
- data/data/gemfile/v1.6.13/Gemfile.lock.archived +0 -745
- data/data/gemfile/v1.6.14/Gemfile +0 -5
- data/data/gemfile/v1.6.14/Gemfile.lock.archived +0 -754
- data/data/gemfile/v1.6.15/Gemfile +0 -5
- data/data/gemfile/v1.6.15/Gemfile.lock.archived +0 -757
- data/data/gemfile/v1.6.2/Gemfile +0 -5
- data/data/gemfile/v1.6.2/Gemfile.lock.archived +0 -718
- data/data/gemfile/v1.6.3/Gemfile +0 -5
- data/data/gemfile/v1.6.3/Gemfile.lock.archived +0 -728
- data/data/gemfile/v1.6.4/Gemfile +0 -5
- data/data/gemfile/v1.6.4/Gemfile.lock.archived +0 -730
- data/data/gemfile/v1.6.5/Gemfile +0 -5
- data/data/gemfile/v1.6.5/Gemfile.lock.archived +0 -733
- data/data/gemfile/v1.6.6/Gemfile +0 -5
- data/data/gemfile/v1.6.6/Gemfile.lock.archived +0 -733
- data/data/gemfile/v1.6.7/Gemfile +0 -5
- data/data/gemfile/v1.6.7/Gemfile.lock.archived +0 -733
- data/data/gemfile/v1.6.9/Gemfile +0 -5
- data/data/gemfile/v1.6.9/Gemfile.lock.archived +0 -744
- data/data/gemfile/v1.7.0/Gemfile +0 -5
- data/data/gemfile/v1.7.0/Gemfile.lock.archived +0 -750
- data/data/gemfile/v1.7.1/Gemfile +0 -5
- data/data/gemfile/v1.7.1/Gemfile.lock.archived +0 -750
- data/data/gemfile/v1.7.2/Gemfile +0 -5
- data/data/gemfile/v1.7.2/Gemfile.lock.archived +0 -747
- data/data/gemfile/v1.7.3/Gemfile +0 -5
- data/data/gemfile/v1.7.3/Gemfile.lock.archived +0 -755
- data/data/gemfile/v1.7.4/Gemfile +0 -5
- data/data/gemfile/v1.7.4/Gemfile.lock.archived +0 -756
- data/data/gemfile/v1.7.5/Gemfile +0 -5
- data/data/gemfile/v1.7.5/Gemfile.lock.archived +0 -759
- data/data/gemfile/v1.7.6/Gemfile +0 -5
- data/data/gemfile/v1.7.6/Gemfile.lock.archived +0 -768
- data/data/gemfile/v1.8.10/Gemfile +0 -5
- data/data/gemfile/v1.8.10/Gemfile.lock.archived +0 -792
- data/data/gemfile/v1.8.11/Gemfile +0 -5
- data/data/gemfile/v1.8.11/Gemfile.lock.archived +0 -862
- data/data/gemfile/v1.8.3/Gemfile +0 -5
- data/data/gemfile/v1.8.3/Gemfile.lock.archived +0 -773
- data/data/gemfile/v1.8.4/Gemfile +0 -5
- data/data/gemfile/v1.8.4/Gemfile.lock.archived +0 -768
- data/data/gemfile/v1.8.5/Gemfile +0 -5
- data/data/gemfile/v1.8.5/Gemfile.lock.archived +0 -768
- data/data/gemfile/v1.8.6/Gemfile +0 -5
- data/data/gemfile/v1.8.6/Gemfile.lock.archived +0 -777
- data/data/gemfile/v1.8.7/Gemfile +0 -5
- data/data/gemfile/v1.8.7/Gemfile.lock.archived +0 -777
- data/data/gemfile/v1.8.8/Gemfile +0 -5
- data/data/gemfile/v1.8.8/Gemfile.lock.archived +0 -778
- data/data/gemfile/v1.8.9/Gemfile +0 -5
- data/data/gemfile/v1.8.9/Gemfile.lock.archived +0 -775
- data/data/gemfile/v1.9.0/Gemfile +0 -5
- data/data/gemfile/v1.9.0/Gemfile.lock.archived +0 -871
- data/data/gemfile/v1.9.1/Gemfile +0 -5
- data/data/gemfile/v1.9.1/Gemfile.lock.archived +0 -906
- data/data/gemfile/v1.9.2/Gemfile +0 -5
- data/data/gemfile/v1.9.2/Gemfile.lock.archived +0 -898
- data/data/gemfile/v1.9.3/Gemfile +0 -5
- data/data/gemfile/v1.9.3/Gemfile.lock.archived +0 -898
- data/data/gemfile/v1.9.4/Gemfile +0 -5
- data/data/gemfile/v1.9.4/Gemfile.lock.archived +0 -901
- data/data/gemfile/v1.9.5/Gemfile +0 -5
- data/data/gemfile/v1.9.5/Gemfile.lock.archived +0 -903
- data/data/gemfile/v1.9.6/Gemfile +0 -5
- data/data/gemfile/v1.9.6/Gemfile.lock.archived +0 -900
- data/data/gemfile/v1.9.7/Gemfile +0 -5
- data/data/gemfile/v1.9.7/Gemfile.lock.archived +0 -922
- data/data/gemfile/v1.9.8/Gemfile +0 -5
- data/data/gemfile/v1.9.8/Gemfile.lock.archived +0 -933
- data/data/gemfile/versions.yaml +0 -751
- data/data/homebrew/versions.yaml +0 -567
- data/data/snap/github_tags.json +0 -42
- data/data/snap/versions.yaml +0 -589
- data/snapcraft-list-copied-from-site.md +0 -101
|
@@ -4,12 +4,14 @@ require 'thor'
|
|
|
4
4
|
require 'json'
|
|
5
5
|
|
|
6
6
|
module Mnenv
|
|
7
|
+
autoload :SnapRepository, 'mnenv/snap_repository'
|
|
8
|
+
autoload :JsonFormatter, 'mnenv/json_formatter'
|
|
9
|
+
autoload :Snap, 'mnenv/snap'
|
|
10
|
+
|
|
7
11
|
class SnapCommand < Thor
|
|
8
12
|
desc 'list', 'List all Snap versions'
|
|
9
13
|
method_option :format, type: :string, aliases: '-f', default: 'text'
|
|
10
14
|
def list
|
|
11
|
-
require_relative '../snap_repository'
|
|
12
|
-
require_relative '../json_formatter'
|
|
13
15
|
repo = SnapRepository.new
|
|
14
16
|
versions = repo.all
|
|
15
17
|
|
|
@@ -25,8 +27,6 @@ module Mnenv
|
|
|
25
27
|
|
|
26
28
|
desc 'refresh', 'Fetch and add new Snap versions (incremental)'
|
|
27
29
|
def refresh
|
|
28
|
-
require_relative '../snap/fetcher'
|
|
29
|
-
require_relative '../snap_repository'
|
|
30
30
|
fetcher = Snap::Fetcher.new
|
|
31
31
|
repo = fetcher.repository
|
|
32
32
|
|
|
@@ -50,7 +50,6 @@ module Mnenv
|
|
|
50
50
|
|
|
51
51
|
desc 'revamp', 'Re-fetch all Snap versions'
|
|
52
52
|
def revamp
|
|
53
|
-
require_relative '../snap/fetcher'
|
|
54
53
|
fetcher = Snap::Fetcher.new
|
|
55
54
|
versions = fetcher.fetch_and_save
|
|
56
55
|
puts "Revamped #{versions.size} Snap versions"
|
|
@@ -58,14 +57,13 @@ module Mnenv
|
|
|
58
57
|
|
|
59
58
|
desc 'update VERSION', 'Update a specific Snap version (all arch/channel combinations)'
|
|
60
59
|
def update(version)
|
|
61
|
-
require_relative '../snap/fetcher'
|
|
62
60
|
fetcher = Snap::Fetcher.new
|
|
63
61
|
versions = fetcher.fetch_all
|
|
64
62
|
targets = versions.select { |v| v.version == version }
|
|
65
63
|
|
|
66
64
|
if targets.empty?
|
|
67
65
|
puts "Snap version #{version} not found in current channel-map"
|
|
68
|
-
puts
|
|
66
|
+
puts 'Note: Historical versions no longer in Snap API cannot be updated'
|
|
69
67
|
exit 1
|
|
70
68
|
end
|
|
71
69
|
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'tty/prompt'
|
|
4
|
+
require_relative '../installer'
|
|
5
|
+
|
|
6
|
+
module Mnenv
|
|
7
|
+
class UninstallCommand < Thor
|
|
8
|
+
namespace :uninstall
|
|
9
|
+
|
|
10
|
+
class_option :source, type: :string, enum: %w[gemfile binary],
|
|
11
|
+
desc: 'Source type to uninstall (gemfile or binary). If not specified, uninstalls all sources.'
|
|
12
|
+
class_option :force, type: :boolean, aliases: '-f', default: false,
|
|
13
|
+
desc: 'Force uninstallation without confirmation'
|
|
14
|
+
|
|
15
|
+
desc 'VERSION', 'Uninstall a specific Metanorma version'
|
|
16
|
+
method_option :source, type: :string, enum: %w[gemfile binary]
|
|
17
|
+
method_option :force, type: :boolean, aliases: '-f', default: false
|
|
18
|
+
def uninstall(version)
|
|
19
|
+
source = options[:source]
|
|
20
|
+
|
|
21
|
+
if source
|
|
22
|
+
# Uninstall specific source
|
|
23
|
+
uninstall_source(version, source)
|
|
24
|
+
else
|
|
25
|
+
# Uninstall all sources for this version
|
|
26
|
+
uninstall_all_sources(version)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Regenerate shims after uninstallation
|
|
30
|
+
ShimManager.new.regenerate_all
|
|
31
|
+
rescue StandardError => e
|
|
32
|
+
warn "Error: #{e.message}"
|
|
33
|
+
exit 1
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def uninstall_source(version, source)
|
|
39
|
+
version_dir = Paths.version_install_dir(version, source)
|
|
40
|
+
|
|
41
|
+
unless Dir.exist?(version_dir)
|
|
42
|
+
puts "Version #{version} (source: #{source}) is not installed."
|
|
43
|
+
return
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
confirm_and_remove(version, source, version_dir)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def uninstall_all_sources(version)
|
|
50
|
+
# Find all installed sources for this version
|
|
51
|
+
installed_sources = find_installed_sources(version)
|
|
52
|
+
|
|
53
|
+
if installed_sources.empty?
|
|
54
|
+
puts "Version #{version} is not installed."
|
|
55
|
+
return
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
if installed_sources.length == 1
|
|
59
|
+
# Only one source, uninstall directly
|
|
60
|
+
source = installed_sources.first
|
|
61
|
+
version_dir = Paths.version_install_dir(version, source)
|
|
62
|
+
confirm_and_remove(version, source, version_dir)
|
|
63
|
+
else
|
|
64
|
+
# Multiple sources, ask which to uninstall
|
|
65
|
+
puts "Version #{version} has multiple sources installed:"
|
|
66
|
+
installed_sources.each do |src|
|
|
67
|
+
puts " - #{src}"
|
|
68
|
+
end
|
|
69
|
+
puts ''
|
|
70
|
+
|
|
71
|
+
prompt = TTY::Prompt.new
|
|
72
|
+
choices = installed_sources.map { |s| { name: s, value: s } }
|
|
73
|
+
choices << { name: 'All sources', value: 'all' }
|
|
74
|
+
|
|
75
|
+
selected = prompt.select('Which source(s) to uninstall?', choices)
|
|
76
|
+
|
|
77
|
+
if selected == 'all'
|
|
78
|
+
installed_sources.each do |src|
|
|
79
|
+
version_dir = Paths.version_install_dir(version, src)
|
|
80
|
+
confirm_and_remove(version, src, version_dir, skip_confirm: options[:force])
|
|
81
|
+
end
|
|
82
|
+
else
|
|
83
|
+
version_dir = Paths.version_install_dir(version, selected)
|
|
84
|
+
confirm_and_remove(version, selected, version_dir)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def find_installed_sources(version)
|
|
90
|
+
sources = []
|
|
91
|
+
%w[gemfile binary].each do |source|
|
|
92
|
+
version_dir = Paths.version_install_dir(version, source)
|
|
93
|
+
sources << source if Dir.exist?(version_dir)
|
|
94
|
+
end
|
|
95
|
+
sources
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def confirm_and_remove(version, source, version_dir, skip_confirm: false)
|
|
99
|
+
unless skip_confirm || options[:force]
|
|
100
|
+
prompt = TTY::Prompt.new
|
|
101
|
+
unless prompt.yes?("Uninstall Metanorma #{version} (#{source})? This cannot be undone.")
|
|
102
|
+
puts 'Uninstallation cancelled.'
|
|
103
|
+
return
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
FileUtils.rm_rf(version_dir)
|
|
108
|
+
puts "Uninstalled Metanorma #{version} (source: #{source})"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'tty/prompt'
|
|
4
|
+
require 'json'
|
|
5
|
+
require_relative '../installer'
|
|
6
|
+
require_relative '../version_resolver'
|
|
7
|
+
|
|
8
|
+
module Mnenv
|
|
9
|
+
class VersionCommand < Thor
|
|
10
|
+
namespace :version
|
|
11
|
+
|
|
12
|
+
class_option :source, type: :string, enum: %w[gemfile binary],
|
|
13
|
+
desc: 'Source type (gemfile or binary)'
|
|
14
|
+
class_option :interactive, type: :boolean, aliases: '-i', default: false,
|
|
15
|
+
desc: 'Interactive mode for version selection'
|
|
16
|
+
|
|
17
|
+
desc 'use VERSION', 'Set Metanorma version for current shell session'
|
|
18
|
+
method_option :source, type: :string, enum: %w[gemfile binary]
|
|
19
|
+
method_option :interactive, type: :boolean, aliases: '-i', default: false
|
|
20
|
+
def use(version = nil)
|
|
21
|
+
version, source = resolve_version_and_source(version, options[:source], options[:interactive])
|
|
22
|
+
|
|
23
|
+
puts "export METANORMA_VERSION=#{version}"
|
|
24
|
+
puts "export METANORMA_SOURCE=#{source}" if source
|
|
25
|
+
puts "# Run this in your shell, or use: eval \"$(mnenv use #{version}#{" --source #{source}" if source})\""
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
desc 'global VERSION', 'Set default Metanorma version globally'
|
|
29
|
+
method_option :source, type: :string, enum: %w[gemfile binary]
|
|
30
|
+
method_option :interactive, type: :boolean, aliases: '-i', default: false
|
|
31
|
+
def global(version = nil)
|
|
32
|
+
version, source = resolve_version_and_source(version, options[:source], options[:interactive])
|
|
33
|
+
verify_installed!(version, source)
|
|
34
|
+
|
|
35
|
+
File.write(Paths::VERSION_FILE, version)
|
|
36
|
+
File.write(Paths::SOURCE_FILE, source) if source
|
|
37
|
+
puts "Global Metanorma version set to #{version}#{source ? " (source: #{source})" : ''}"
|
|
38
|
+
rescue StandardError => e
|
|
39
|
+
warn "Error: #{e.message}"
|
|
40
|
+
exit 1
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
desc 'local VERSION', 'Set Metanorma version for current directory'
|
|
44
|
+
method_option :source, type: :string, enum: %w[gemfile binary]
|
|
45
|
+
method_option :interactive, type: :boolean, aliases: '-i', default: false
|
|
46
|
+
def local(version = nil)
|
|
47
|
+
version, source = resolve_version_and_source(version, options[:source], options[:interactive])
|
|
48
|
+
verify_installed!(version, source)
|
|
49
|
+
|
|
50
|
+
File.write('.metanorma-version', version)
|
|
51
|
+
File.write('.metanorma-source', source) if source
|
|
52
|
+
puts "Local Metanorma version set to #{version}#{source ? " (source: #{source})" : ''}"
|
|
53
|
+
puts "Created .metanorma-version#{source ? ' and .metanorma-source' : ''}"
|
|
54
|
+
rescue StandardError => e
|
|
55
|
+
warn "Error: #{e.message}"
|
|
56
|
+
exit 1
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
desc 'versions', 'List all installed Metanorma versions'
|
|
60
|
+
method_option :format, type: :string, aliases: '-f', default: 'text',
|
|
61
|
+
desc: 'Output format (text or json)'
|
|
62
|
+
def versions
|
|
63
|
+
installed = list_installed_versions
|
|
64
|
+
|
|
65
|
+
if installed.empty?
|
|
66
|
+
puts 'No versions installed.'
|
|
67
|
+
puts "\nRun: mnenv install --list"
|
|
68
|
+
return
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
current_version, current_source = resolver.resolve
|
|
72
|
+
|
|
73
|
+
case options[:format]
|
|
74
|
+
when 'json'
|
|
75
|
+
output = {
|
|
76
|
+
'current_version' => current_version,
|
|
77
|
+
'current_source' => current_source,
|
|
78
|
+
'installed' => installed.map do |version, sources|
|
|
79
|
+
sources.map do |source|
|
|
80
|
+
{
|
|
81
|
+
'version' => version,
|
|
82
|
+
'source' => source,
|
|
83
|
+
'current' => version == current_version && source == current_source
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
end.flatten
|
|
87
|
+
}
|
|
88
|
+
puts JSON.pretty_generate(output)
|
|
89
|
+
else
|
|
90
|
+
puts 'Installed Metanorma versions:'
|
|
91
|
+
|
|
92
|
+
installed.sort.reverse.each do |version, sources|
|
|
93
|
+
sources.sort.each do |source|
|
|
94
|
+
is_current = version == current_version && source == current_source
|
|
95
|
+
marker = is_current ? '* ' : ' '
|
|
96
|
+
puts " #{marker}#{version} (source: #{source})"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
puts "\nCurrent version: #{current_version || 'none'}"
|
|
101
|
+
puts "Current source: #{current_source || 'none'}"
|
|
102
|
+
puts "\nTo see available versions, run: mnenv install --list"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
# Get the shared resolver instance
|
|
109
|
+
def resolver
|
|
110
|
+
@resolver ||= VersionResolver.new
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# List installed versions with their sources
|
|
114
|
+
# Uses Paths::INSTALLED_DIR with new naming convention: <version>-<source>
|
|
115
|
+
def list_installed_versions
|
|
116
|
+
return {} unless Dir.exist?(Paths::INSTALLED_DIR)
|
|
117
|
+
|
|
118
|
+
versions = Hash.new { |h, k| h[k] = [] }
|
|
119
|
+
|
|
120
|
+
Dir.glob(File.join(Paths::INSTALLED_DIR, '*')).each do |dir|
|
|
121
|
+
next unless File.directory?(dir)
|
|
122
|
+
|
|
123
|
+
dir_name = File.basename(dir)
|
|
124
|
+
version, source = Paths.parse_version_dir(dir_name)
|
|
125
|
+
|
|
126
|
+
# Only include valid version-source directories
|
|
127
|
+
next unless version && source
|
|
128
|
+
|
|
129
|
+
versions[version] << source
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
versions
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def resolve_version_and_source(version, source, interactive)
|
|
136
|
+
version, source = select_version_interactive if interactive || version.nil?
|
|
137
|
+
source ||= resolver.resolve_source
|
|
138
|
+
[version, source]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def select_version_interactive
|
|
142
|
+
prompt = TTY::Prompt.new
|
|
143
|
+
installed = list_installed_versions
|
|
144
|
+
|
|
145
|
+
raise 'No versions installed. Run: mnenv install --list' if installed.empty?
|
|
146
|
+
|
|
147
|
+
choices = installed.flat_map do |version, sources|
|
|
148
|
+
sources.map do |src|
|
|
149
|
+
{ name: "#{version} (#{src})", value: [version, src] }
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
prompt.select('Select a version:', choices)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def verify_installed!(version, source)
|
|
157
|
+
version_dir = Paths.version_install_dir(version, source)
|
|
158
|
+
return if Dir.exist?(version_dir)
|
|
159
|
+
|
|
160
|
+
raise "Version #{version} (source: #{source}) is not installed. Run: mnenv install #{version} --source #{source}"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def installed_versions
|
|
164
|
+
list_installed_versions.keys.sort
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
data/lib/mnenv/commands.rb
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
module Mnenv
|
|
4
|
+
autoload :GemfileCommand, 'mnenv/commands/gemfile_command'
|
|
5
|
+
autoload :SnapCommand, 'mnenv/commands/snap_command'
|
|
6
|
+
autoload :HomebrewCommand, 'mnenv/commands/homebrew_command'
|
|
7
|
+
autoload :ChocolateyCommand, 'mnenv/commands/chocolatey_command'
|
|
8
|
+
autoload :InstallCommand, 'mnenv/commands/install_command'
|
|
9
|
+
autoload :VersionCommand, 'mnenv/commands/version_command'
|
|
10
|
+
autoload :UninstallCommand, 'mnenv/commands/uninstall_command'
|
|
11
|
+
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'English'
|
|
3
4
|
require_relative '../logger'
|
|
4
5
|
require_relative '../gemfile_repository'
|
|
5
6
|
require_relative './fetcher'
|
|
@@ -36,7 +37,7 @@ module Mnenv
|
|
|
36
37
|
|
|
37
38
|
def extract_incremental
|
|
38
39
|
remote_versions = fetcher.fetch_all
|
|
39
|
-
existing_versions = repository.all.select
|
|
40
|
+
existing_versions = repository.all.select(&:exists_locally?).map(&:version)
|
|
40
41
|
|
|
41
42
|
missing = remote_versions.reject { |v| existing_versions.include?(v.version) }
|
|
42
43
|
|
|
@@ -74,12 +75,18 @@ module Mnenv
|
|
|
74
75
|
|
|
75
76
|
cleanup_docker_image(version.version)
|
|
76
77
|
|
|
78
|
+
# Update version metadata after successful extraction
|
|
79
|
+
version.gemfile_exists = true
|
|
80
|
+
# Store relative paths (relative to data/gemfile directory)
|
|
81
|
+
version.gemfile_path = "v#{version.version}/Gemfile"
|
|
82
|
+
version.gemfile_lock_path = "v#{version.version}/Gemfile.lock.archived"
|
|
83
|
+
|
|
77
84
|
repository.save(version)
|
|
78
85
|
end
|
|
79
86
|
|
|
80
87
|
def pull_docker_image(version_number)
|
|
81
88
|
system('docker', 'pull', "#{Fetcher::DOCKER_IMAGE}:#{version_number}", out: File::NULL)
|
|
82
|
-
raise 'Failed to pull Docker image' unless
|
|
89
|
+
raise 'Failed to pull Docker image' unless $CHILD_STATUS.success?
|
|
83
90
|
end
|
|
84
91
|
|
|
85
92
|
def extract_gemfiles(version)
|
|
@@ -89,7 +96,7 @@ module Mnenv
|
|
|
89
96
|
cmd = "docker run --rm --entrypoint sh #{Fetcher::DOCKER_IMAGE}:#{version.version} -c '#{script}'"
|
|
90
97
|
output = `#{cmd}`
|
|
91
98
|
|
|
92
|
-
raise "Extraction failed for #{version.version}" unless
|
|
99
|
+
raise "Extraction failed for #{version.version}" unless $CHILD_STATUS.success?
|
|
93
100
|
|
|
94
101
|
gemfile, gemfile_lock = parse_gemfile_output(output)
|
|
95
102
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mnenv
|
|
4
|
+
class Installer
|
|
5
|
+
class InstallationError < StandardError; end
|
|
6
|
+
class DevelopmentToolsMissing < InstallationError; end
|
|
7
|
+
|
|
8
|
+
attr_reader :version, :source
|
|
9
|
+
|
|
10
|
+
def initialize(version, source: nil, target_dir: nil)
|
|
11
|
+
@version = version
|
|
12
|
+
@source = source || default_source
|
|
13
|
+
@target_dir = target_dir || default_target_dir
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def install
|
|
17
|
+
verify_prerequisites!
|
|
18
|
+
create_install_directory
|
|
19
|
+
perform_installation
|
|
20
|
+
regenerate_shims
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def installed?
|
|
24
|
+
Dir.exist?(version_dir)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def default_target_dir
|
|
30
|
+
@default_target_dir ||= Paths.version_install_dir(version, source)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def version_dir
|
|
34
|
+
@target_dir
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def verify_prerequisites!
|
|
38
|
+
raise NotImplementedError, "#{self.class} must implement verify_prerequisites!"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def perform_installation
|
|
42
|
+
raise NotImplementedError, "#{self.class} must implement perform_installation!"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def create_install_directory
|
|
46
|
+
FileUtils.mkdir_p(version_dir)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def regenerate_shims
|
|
50
|
+
ShimManager.new.regenerate_all
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def default_source
|
|
54
|
+
# Try to read from ~/.mnenv/source, else default to gemfile
|
|
55
|
+
if File.exist?(Paths::SOURCE_FILE)
|
|
56
|
+
File.read(Paths::SOURCE_FILE).strip
|
|
57
|
+
else
|
|
58
|
+
'gemfile' # Default: faster for devs
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../source_registry'
|
|
4
|
+
|
|
5
|
+
module Mnenv
|
|
6
|
+
# Factory for creating installer instances based on source type
|
|
7
|
+
# Uses SourceRegistry for extensibility - new sources just need to register
|
|
8
|
+
class InstallerFactory
|
|
9
|
+
class UnknownSourceError < StandardError; end
|
|
10
|
+
|
|
11
|
+
# Create an installer for the given version and source
|
|
12
|
+
# @param version [String] The version to install
|
|
13
|
+
# @param source [String] The source type (e.g., 'gemfile', 'binary')
|
|
14
|
+
# @param target_dir [String, nil] Optional custom target directory
|
|
15
|
+
# @return [Installer] An installer instance
|
|
16
|
+
# @raise [UnknownSourceError] If source is not registered
|
|
17
|
+
def self.create(version, source:, target_dir: nil)
|
|
18
|
+
installer_class = SourceRegistry.installer(source.to_s)
|
|
19
|
+
|
|
20
|
+
unless installer_class
|
|
21
|
+
available = SourceRegistry.all_names.join(', ')
|
|
22
|
+
raise Installer::InstallationError,
|
|
23
|
+
"Unknown source: #{source}. Available sources: #{available}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
installer_class.new(version, source: source, target_dir: target_dir)
|
|
27
|
+
rescue SourceRegistry::UnknownSourceError
|
|
28
|
+
available = SourceRegistry.all_names.join(', ')
|
|
29
|
+
raise Installer::InstallationError,
|
|
30
|
+
"Unknown source: #{source}. Available sources: #{available}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Check if a source is supported
|
|
34
|
+
# @param source [String] The source type
|
|
35
|
+
# @return [Boolean]
|
|
36
|
+
def self.supported?(source)
|
|
37
|
+
SourceRegistry.registered?(source.to_s)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Get list of supported sources
|
|
41
|
+
# @return [Array<String>]
|
|
42
|
+
def self.supported_sources
|
|
43
|
+
SourceRegistry.all_names
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mnenv
|
|
4
|
+
autoload :Installer, 'mnenv/installer/base'
|
|
5
|
+
autoload :InstallerFactory, 'mnenv/installer/factory'
|
|
6
|
+
autoload :ShimManager, 'mnenv/shim_manager'
|
|
7
|
+
|
|
8
|
+
module Installers
|
|
9
|
+
autoload :GemfileInstaller, 'mnenv/installers/gemfile_installer'
|
|
10
|
+
autoload :BinaryInstaller, 'mnenv/installers/binary_installer'
|
|
11
|
+
end
|
|
12
|
+
end
|