fhir_packages_manager 0.1.0 → 0.3.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/CHANGELOG.md +24 -1
- data/README.md +24 -2
- data/lib/fhir_packages_manager/cli.rb +31 -2
- data/lib/fhir_packages_manager/fetch_result.rb +8 -3
- data/lib/fhir_packages_manager/manager.rb +62 -3
- data/lib/fhir_packages_manager/registry.rb +8 -0
- data/lib/fhir_packages_manager/version.rb +1 -1
- data/sig/fhir_packages_manager/cli.rbs +6 -0
- data/sig/fhir_packages_manager/fetch_result.rbs +2 -0
- data/sig/fhir_packages_manager/manager.rbs +14 -0
- data/sig/fhir_packages_manager/registry.rbs +2 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7d65db7b1f5b29ac8f0064d0cfd224529dc336b2b88c7a4de952206fe81477f7
|
|
4
|
+
data.tar.gz: c16f3255dce19c1058b3781b5695e0c059e6d76b45dc63ff1c35a94891eacaa3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 016d41a5b7f0aed1ce89fa445e149ea85b4e991c9c61d9ba111fab699b113e88b8e62c650f8b08314e5246ca875927dbe0abb9bbdd92b9d68dbf8306c502f68c
|
|
7
|
+
data.tar.gz: bed4d3fb7c4f7abb708837a2ebbd7ecac247c18284b16e51967b216240f98878090df35765dcfdfdf3c8700c41ab785176d05d58747dff6a076ee636ef1ff29a
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.3.0] - 2026-07-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `sync` CLI command and `Manager#sync`: downloads every non-ignored version of a package not
|
|
15
|
+
already present in the destination folder (checked as `name-version.tgz`, the same naming
|
|
16
|
+
`fetch` uses), reusing `#fetch` for the actual download of each missing version. Output reuses
|
|
17
|
+
`fetch`'s `OK`/`SKIP`/`MISS`/`ERR` lines, with `SKIP` covering both an ignored version and one
|
|
18
|
+
already on disk. (#3)
|
|
19
|
+
|
|
20
|
+
## [0.2.0] - 2026-07-10
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- `list` CLI command and `Manager#list_versions`/`Registry#versions`: lists every version of a
|
|
25
|
+
package published across all configured registries, without needing a version suffix on the
|
|
26
|
+
spec. Respects the ignore list the same way `fetch` does (whole-package or single-version
|
|
27
|
+
entries). (#1)
|
|
28
|
+
|
|
8
29
|
## [0.1.0] - 2026-07-10
|
|
9
30
|
|
|
10
31
|
### Added
|
|
@@ -39,5 +60,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
39
60
|
- **Documentation**: YARD doc comments across the public API, and a Codecov/docs badge pair
|
|
40
61
|
in the README.
|
|
41
62
|
|
|
42
|
-
[Unreleased]: https://github.com/projkov/fhir_packages_manager/compare/v0.
|
|
63
|
+
[Unreleased]: https://github.com/projkov/fhir_packages_manager/compare/v0.3.0...HEAD
|
|
64
|
+
[0.3.0]: https://github.com/projkov/fhir_packages_manager/compare/v0.2.0...v0.3.0
|
|
65
|
+
[0.2.0]: https://github.com/projkov/fhir_packages_manager/compare/v0.1.0...v0.2.0
|
|
43
66
|
[0.1.0]: https://github.com/projkov/fhir_packages_manager/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# FHIR Packages Manager
|
|
2
2
|
|
|
3
|
+
[](https://badge.fury.io/rb/fhir_packages_manager)
|
|
4
|
+
[](fhir_packages_manager.gemspec)
|
|
3
5
|
[](https://codecov.io/gh/projkov/fhir_packages_manager)
|
|
4
6
|
[](https://projkov.github.io/fhir_packages_manager/)
|
|
5
7
|
|
|
@@ -42,6 +44,10 @@ result.status # => :downloaded, :ignored, :not_found, or :error
|
|
|
42
44
|
result.path # => "./fhir_packages/hl7.fhir.us.core-6.1.0.tgz" when downloaded
|
|
43
45
|
|
|
44
46
|
manager.fetch_all(["hl7.fhir.us.core@6.1.0", "hl7.fhir.r4.core"]) # bare name = latest
|
|
47
|
+
|
|
48
|
+
manager.list_versions("hl7.fhir.us.core") # => {"https://packages.fhir.org" => ["1.0.0", "1.0.1", ...], ...}
|
|
49
|
+
|
|
50
|
+
manager.sync("hl7.fhir.us.core") # fetches every non-ignored version not already in destination
|
|
45
51
|
```
|
|
46
52
|
|
|
47
53
|
The ignore list file (YAML or JSON) is a flat array where a bare string
|
|
@@ -60,8 +66,18 @@ exe/fhir_packages_manager check hl7.fhir.us.core@6.1.0 -r https://packages.fhir.
|
|
|
60
66
|
exe/fhir_packages_manager fetch hl7.fhir.us.core@6.1.0 hl7.fhir.r4.core \
|
|
61
67
|
-r https://packages.fhir.org -r https://packages.simplifier.net \
|
|
62
68
|
-d ./fhir_packages -i fhir_packages_ignore.yml
|
|
69
|
+
exe/fhir_packages_manager list hl7.fhir.us.core \
|
|
70
|
+
-r https://packages.fhir.org -r https://packages.simplifier.net
|
|
71
|
+
exe/fhir_packages_manager sync hl7.fhir.us.core \
|
|
72
|
+
-r https://packages.fhir.org -d ./fhir_packages -i fhir_packages_ignore.yml
|
|
63
73
|
```
|
|
64
74
|
|
|
75
|
+
`sync` downloads every non-ignored version of a package that isn't already in the destination
|
|
76
|
+
folder (checked as `name-version.tgz`, the same naming `fetch` uses — keep that naming if you
|
|
77
|
+
move the files afterward, since a bare `version.tgz` can collide between two IGs that happen to
|
|
78
|
+
publish the same version number). Its output reuses `fetch`'s `OK`/`SKIP`/`MISS`/`ERR` lines,
|
|
79
|
+
with `SKIP` covering both an ignored version and one already on disk.
|
|
80
|
+
|
|
65
81
|
### Docker
|
|
66
82
|
|
|
67
83
|
The CLI is also published as a container image, so it can be used without installing Ruby
|
|
@@ -74,7 +90,13 @@ docker run --rm -v "$(pwd)/fhir_packages:/fhir_packages" ghcr.io/projkov/fhir_pa
|
|
|
74
90
|
fetch hl7.fhir.us.core@6.1.0 -r https://packages.fhir.org -d /fhir_packages
|
|
75
91
|
```
|
|
76
92
|
|
|
77
|
-
Mount a volume (as above) to get downloaded `.tgz` files back out onto the host.
|
|
93
|
+
Mount a volume (as above) to get downloaded `.tgz` files back out onto the host. `latest` and
|
|
94
|
+
version tags are built for both `linux/amd64` and `linux/arm64` (e.g. Apple Silicon).
|
|
95
|
+
|
|
96
|
+
To get a fresh image built from an unreleased commit (without cutting a real gem release),
|
|
97
|
+
run the **Docker Snapshot** workflow manually from the Actions tab. It re-runs the test/quality
|
|
98
|
+
gate, then pushes `ghcr.io/projkov/fhir_packages_manager:<gem-version>-<commit-sha>` (e.g.
|
|
99
|
+
`0.1.0-9649122`) — never `latest`, so it can't be mistaken for a real release.
|
|
78
100
|
|
|
79
101
|
#### Docker Compose
|
|
80
102
|
|
|
@@ -7,7 +7,7 @@ module FhirPackagesManager
|
|
|
7
7
|
# See the "CLI" section of the README for usage examples.
|
|
8
8
|
class CLI
|
|
9
9
|
# @return [Array<String>] the supported subcommands
|
|
10
|
-
COMMANDS = %w[fetch check].freeze
|
|
10
|
+
COMMANDS = %w[fetch check list sync].freeze
|
|
11
11
|
|
|
12
12
|
# @return [String] usage text shown on --help and on invalid invocations
|
|
13
13
|
BANNER = <<~USAGE
|
|
@@ -16,6 +16,8 @@ module FhirPackagesManager
|
|
|
16
16
|
Commands:
|
|
17
17
|
fetch Download packages into the destination folder
|
|
18
18
|
check Report which registry (if any) has each package/version
|
|
19
|
+
list List every version of a package available across registries
|
|
20
|
+
sync Download every non-ignored version not already in the destination folder
|
|
19
21
|
|
|
20
22
|
USAGE
|
|
21
23
|
|
|
@@ -53,6 +55,10 @@ module FhirPackagesManager
|
|
|
53
55
|
fetch(package_specs)
|
|
54
56
|
when 'check'
|
|
55
57
|
check(package_specs)
|
|
58
|
+
when 'list'
|
|
59
|
+
list(package_specs)
|
|
60
|
+
when 'sync'
|
|
61
|
+
sync(package_specs)
|
|
56
62
|
end
|
|
57
63
|
end
|
|
58
64
|
|
|
@@ -110,9 +116,11 @@ module FhirPackagesManager
|
|
|
110
116
|
|
|
111
117
|
def fetch_line(result)
|
|
112
118
|
package = result.package
|
|
119
|
+
path = result.path
|
|
113
120
|
case result.status
|
|
114
|
-
when :downloaded then "OK #{package} -> #{
|
|
121
|
+
when :downloaded then "OK #{package} -> #{path} (#{result.registry})"
|
|
115
122
|
when :ignored then "SKIP #{package} (ignored)"
|
|
123
|
+
when :skipped then "SKIP #{package} (already exists at #{path})"
|
|
116
124
|
when :not_found then "MISS #{package} (not found in any registry)"
|
|
117
125
|
when :error then "ERR #{package}: #{result.error}"
|
|
118
126
|
end
|
|
@@ -132,5 +140,26 @@ module FhirPackagesManager
|
|
|
132
140
|
registry, version = found
|
|
133
141
|
"AVAILABLE #{package.name}@#{version} (#{registry.base_url})"
|
|
134
142
|
end
|
|
143
|
+
|
|
144
|
+
def list(package_specs)
|
|
145
|
+
package_specs.each do |spec|
|
|
146
|
+
name = Package.parse(spec).name
|
|
147
|
+
list_lines(name, manager.list_versions(name)).each { |line| puts line }
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def list_lines(name, versions_by_registry)
|
|
152
|
+
return ["NONE #{name} (not found in any registry)"] if versions_by_registry.empty?
|
|
153
|
+
|
|
154
|
+
versions_by_registry.map do |base_url, versions|
|
|
155
|
+
"FOUND #{name} @ #{base_url}: #{versions.sort.join(', ')}"
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def sync(package_specs)
|
|
160
|
+
results = package_specs.flat_map { |spec| manager.sync(Package.parse(spec).name) }
|
|
161
|
+
results.each { |result| puts fetch_line(result) }
|
|
162
|
+
exit 1 if results.any? { |result| result.not_found? || result.error? }
|
|
163
|
+
end
|
|
135
164
|
end
|
|
136
165
|
end
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module FhirPackagesManager
|
|
4
|
-
# Outcome of {Manager#fetch} for a single package.
|
|
4
|
+
# Outcome of {Manager#fetch} (or {Manager#sync}) for a single package/version.
|
|
5
5
|
#
|
|
6
6
|
# @!attribute package
|
|
7
7
|
# @return [Package] the package that was requested (version resolved, when downloaded)
|
|
8
8
|
# @!attribute status
|
|
9
|
-
# @return [Symbol] one of :downloaded, :ignored, :not_found, :error
|
|
9
|
+
# @return [Symbol] one of :downloaded, :ignored, :skipped, :not_found, :error
|
|
10
10
|
# @!attribute registry
|
|
11
11
|
# @return [String, nil] the base URL of the registry that served the package, if downloaded
|
|
12
12
|
# @!attribute path
|
|
13
|
-
# @return [String, nil] where the tarball was
|
|
13
|
+
# @return [String, nil] where the tarball was (or already is) on disk, if downloaded/skipped
|
|
14
14
|
# @!attribute error
|
|
15
15
|
# @return [String, nil] the error message, when status is :error
|
|
16
16
|
class FetchResult < Struct.new(:package, :status, :registry, :path, :error, keyword_init: true)
|
|
@@ -24,6 +24,11 @@ module FhirPackagesManager
|
|
|
24
24
|
status == :ignored
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# @return [Boolean] true if {Manager#sync} skipped it because it was already downloaded
|
|
28
|
+
def skipped?
|
|
29
|
+
status == :skipped
|
|
30
|
+
end
|
|
31
|
+
|
|
27
32
|
# @return [Boolean] true if no registry had the package/version
|
|
28
33
|
def not_found?
|
|
29
34
|
status == :not_found
|
|
@@ -60,6 +60,40 @@ module FhirPackagesManager
|
|
|
60
60
|
packages.map { |package| fetch(package) }
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
# Lists every version of a package published across all configured registries, skipping
|
|
64
|
+
# registries that don't have it at all and filtering out any ignored versions.
|
|
65
|
+
#
|
|
66
|
+
# @param name [String] the package name
|
|
67
|
+
# @return [Hash{String => Array<String>}] versions, keyed by registry base_url, for
|
|
68
|
+
# registries that have at least one non-ignored version; empty if the whole package is
|
|
69
|
+
# ignored or no registry has it
|
|
70
|
+
# @raise [HttpError] if a registry that does have the package fails for another reason
|
|
71
|
+
# (mirrors {#find_registry}/{Registry#version?}, which likewise only treat "not found" as
|
|
72
|
+
# an expected, non-raising outcome)
|
|
73
|
+
def list_versions(name)
|
|
74
|
+
result = {} # : Hash[String, Array[String]]
|
|
75
|
+
return result if ignored?(Package.new(name, nil))
|
|
76
|
+
|
|
77
|
+
registries.each_with_object(result) do |registry, found|
|
|
78
|
+
versions = available_versions(registry, name)
|
|
79
|
+
found[registry.base_url] = versions unless versions.empty?
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Downloads every non-ignored version of a package that isn't already present in
|
|
84
|
+
# {#destination} (as `name-version.tgz`, the same convention {#fetch} downloads use).
|
|
85
|
+
# A whole-package ignore skips it entirely, without querying any registry; an individual
|
|
86
|
+
# ignored version instead surfaces as a normal `:ignored` {FetchResult}, same as {#fetch}.
|
|
87
|
+
#
|
|
88
|
+
# @param name [String] the package name
|
|
89
|
+
# @return [Array<FetchResult>] one result per version considered, across all registries
|
|
90
|
+
def sync(name)
|
|
91
|
+
whole_package = Package.new(name, nil)
|
|
92
|
+
return [FetchResult.new(package: whole_package, status: :ignored)] if ignored?(whole_package)
|
|
93
|
+
|
|
94
|
+
candidate_versions(name).map { |version| sync_version(Package.new(name, version)) }
|
|
95
|
+
end
|
|
96
|
+
|
|
63
97
|
private
|
|
64
98
|
|
|
65
99
|
def fetch_package(package)
|
|
@@ -77,13 +111,38 @@ module FhirPackagesManager
|
|
|
77
111
|
!!ignore_list&.ignored?(package.name, package.version)
|
|
78
112
|
end
|
|
79
113
|
|
|
114
|
+
def available_versions(registry, name)
|
|
115
|
+
raw_versions(registry, name).reject { |version| ignored?(Package.new(name, version)) }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def raw_versions(registry, name)
|
|
119
|
+
registry.versions(name)
|
|
120
|
+
rescue PackageNotFoundError
|
|
121
|
+
[]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def candidate_versions(name)
|
|
125
|
+
registries.flat_map { |registry| raw_versions(registry, name) }.uniq
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def sync_version(package)
|
|
129
|
+
existing_path = download_path(package)
|
|
130
|
+
return FetchResult.new(package: package, status: :skipped, path: existing_path) if File.exist?(existing_path)
|
|
131
|
+
|
|
132
|
+
fetch(package)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def download_path(package)
|
|
136
|
+
File.join(destination, "#{package.name}-#{package.version}.tgz")
|
|
137
|
+
end
|
|
138
|
+
|
|
80
139
|
def download_result(package, found)
|
|
81
140
|
registry, resolved_version = found
|
|
82
141
|
name = package.name
|
|
83
|
-
|
|
142
|
+
resolved_package = Package.new(name, resolved_version)
|
|
143
|
+
target = download_path(resolved_package)
|
|
84
144
|
registry.download(name, resolved_version, target)
|
|
85
|
-
FetchResult.new(package:
|
|
86
|
-
registry: registry.base_url, path: target)
|
|
145
|
+
FetchResult.new(package: resolved_package, status: :downloaded, registry: registry.base_url, path: target)
|
|
87
146
|
end
|
|
88
147
|
end
|
|
89
148
|
end
|
|
@@ -49,6 +49,14 @@ module FhirPackagesManager
|
|
|
49
49
|
nil
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
# @param name [String] the package name
|
|
53
|
+
# @return [Array<String>] every version published for this package on this registry
|
|
54
|
+
# @raise [PackageNotFoundError] if the package doesn't exist on this registry
|
|
55
|
+
# @raise [HttpError] for any other non-2xx/3xx response
|
|
56
|
+
def versions(name)
|
|
57
|
+
metadata(name)['versions']&.keys || []
|
|
58
|
+
end
|
|
59
|
+
|
|
52
60
|
# @param name [String] the package name
|
|
53
61
|
# @param version [String] an exact version, as returned by {#version?}
|
|
54
62
|
# @return [String] the tarball's download URL
|
|
@@ -30,5 +30,11 @@ module FhirPackagesManager
|
|
|
30
30
|
def check: (Array[String] package_specs) -> void
|
|
31
31
|
|
|
32
32
|
def check_line: (Package package, [Registry, String]? found) -> String
|
|
33
|
+
|
|
34
|
+
def list: (Array[String] package_specs) -> void
|
|
35
|
+
|
|
36
|
+
def list_lines: (String name, Hash[String, Array[String]] versions_by_registry) -> Array[String]
|
|
37
|
+
|
|
38
|
+
def sync: (Array[String] package_specs) -> void
|
|
33
39
|
end
|
|
34
40
|
end
|
|
@@ -14,12 +14,26 @@ module FhirPackagesManager
|
|
|
14
14
|
|
|
15
15
|
def fetch_all: (Array[String | Package] packages) -> Array[FetchResult]
|
|
16
16
|
|
|
17
|
+
def list_versions: (String name) -> Hash[String, Array[String]]
|
|
18
|
+
|
|
19
|
+
def sync: (String name) -> Array[FetchResult]
|
|
20
|
+
|
|
17
21
|
private
|
|
18
22
|
|
|
19
23
|
def fetch_package: (Package package) -> FetchResult
|
|
20
24
|
|
|
21
25
|
def ignored?: (Package package) -> bool
|
|
22
26
|
|
|
27
|
+
def available_versions: (Registry registry, String name) -> Array[String]
|
|
28
|
+
|
|
29
|
+
def raw_versions: (Registry registry, String name) -> Array[String]
|
|
30
|
+
|
|
31
|
+
def candidate_versions: (String name) -> Array[String]
|
|
32
|
+
|
|
33
|
+
def sync_version: (Package package) -> FetchResult
|
|
34
|
+
|
|
35
|
+
def download_path: (Package package) -> String
|
|
36
|
+
|
|
23
37
|
def download_result: (Package package, [Registry, String] found) -> FetchResult
|
|
24
38
|
end
|
|
25
39
|
end
|
|
@@ -10,6 +10,8 @@ module FhirPackagesManager
|
|
|
10
10
|
|
|
11
11
|
def version?: (String name, ?String? version) -> String?
|
|
12
12
|
|
|
13
|
+
def versions: (String name) -> Array[String]
|
|
14
|
+
|
|
13
15
|
def tarball_url: (String name, String version) -> String
|
|
14
16
|
|
|
15
17
|
def download: (String name, String version, String destination_path) -> String
|