pray-cli 1.13.0 → 1.14.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 +6 -1
- data/lib/pray/manifest_json.rb +2 -2
- data/lib/pray/publish.rb +53 -5
- data/lib/pray/registry.rb +17 -1
- data/lib/pray/render_content.rb +1 -1
- data/lib/pray/serve_federation.rb +4 -4
- data/lib/pray/version.rb +2 -2
- 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: cc753f65405f7052d882da07b3293b66c69e60cb26b8fe865919000323f2da1a
|
|
4
|
+
data.tar.gz: 91586f374cc5a677b0f5d2b722a4ba5da5912d8bd3664eac0ccd48b864889c65
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a84c2945dba3386f31dfdfc08d603ecee88d2be1e9898c20a993cd9dba9149ae4966b9cf9e02ba8c28905b7a1c81e2f5158bdf2eff8b36fe7f470e0bbd82ab6a
|
|
7
|
+
data.tar.gz: 48f107b5721d69c62b83db0f99247f9c4821eea42f85d3459cac09b7441badc3fa092965f877b1d3ae3fb0d38f391e8f65ae76cc21a6fd1595799dd2de0c6324
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 1.14.0 (2026-09-14)
|
|
4
|
+
|
|
5
|
+
- Keep an unchanged package version's artifact, first-publish time, and yank when `pray publish` runs again (RFC 0061).
|
|
6
|
+
- Write `published_at` as UTC Unix seconds in registry and federation JSON.
|
|
7
|
+
- Collapse trailing blank lines when composing so a composed file ends with a single newline, matching the other CLIs.
|
|
8
|
+
- Order `symbols` before `render` in the canonical manifest JSON so `manifest_hash` matches the other CLIs.
|
|
4
9
|
|
|
5
10
|
## 1.13.0 (2026-09-08)
|
|
6
11
|
|
data/lib/pray/manifest_json.rb
CHANGED
|
@@ -14,10 +14,10 @@ module Pray
|
|
|
14
14
|
"sources" => manifest.sources.map { |source| source_fields(source) },
|
|
15
15
|
"targets" => manifest.targets.map { |target| target_fields(target) },
|
|
16
16
|
"packages" => manifest.packages.map { |package| package_fields(package) },
|
|
17
|
-
"local" => manifest.local.map { |entry| local_fields(entry) }
|
|
18
|
-
"render" => render_fields(manifest.render)
|
|
17
|
+
"local" => manifest.local.map { |entry| local_fields(entry) }
|
|
19
18
|
}
|
|
20
19
|
fields["symbols"] = manifest.symbols.sort.to_h unless manifest.symbols.empty?
|
|
20
|
+
fields["render"] = render_fields(manifest.render)
|
|
21
21
|
fields
|
|
22
22
|
end
|
|
23
23
|
|
data/lib/pray/publish.rb
CHANGED
|
@@ -20,13 +20,21 @@ module Pray
|
|
|
20
20
|
package_names = index.packages.to_set
|
|
21
21
|
|
|
22
22
|
project.packages.each do |package|
|
|
23
|
-
archive_bytes = Archive.build_package_archive_bytes(package)
|
|
24
23
|
artifact_path = registry_artifact_path(package.declaration.name, package.spec.version)
|
|
25
|
-
artifact_output_path = File.join(root, artifact_path)
|
|
26
|
-
write_output_bytes(artifact_output_path, archive_bytes)
|
|
27
|
-
|
|
28
24
|
metadata_path = registry_metadata_path(root, package.declaration.name)
|
|
29
25
|
metadata = load_registry_package_metadata(metadata_path, package.declaration.name)
|
|
26
|
+
existing = metadata.versions.find { |entry| entry.version == package.spec.version }
|
|
27
|
+
stored_artifact = stored_package_artifact(root, artifact_path, package, existing)
|
|
28
|
+
if stored_artifact && stored_publish_matches?(
|
|
29
|
+
stored_artifact, package, signer, signer_fingerprint, existing
|
|
30
|
+
)
|
|
31
|
+
package_names << package.declaration.name
|
|
32
|
+
write_registry_package_metadata(metadata_path, metadata)
|
|
33
|
+
next
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
archive_bytes = Archive.build_package_archive_bytes(package)
|
|
37
|
+
write_output_bytes(File.join(root, artifact_path), archive_bytes)
|
|
30
38
|
version_entry = published_registry_package_version(
|
|
31
39
|
package,
|
|
32
40
|
signer,
|
|
@@ -34,6 +42,7 @@ module Pray
|
|
|
34
42
|
archive_bytes,
|
|
35
43
|
artifact_path
|
|
36
44
|
)
|
|
45
|
+
preserve_existing_publish_metadata(metadata, version_entry, stored_artifact)
|
|
37
46
|
metadata.versions.reject! { |entry| entry.version == version_entry.version }
|
|
38
47
|
metadata.versions << version_entry
|
|
39
48
|
write_registry_package_metadata(metadata_path, metadata)
|
|
@@ -81,11 +90,50 @@ module Pray
|
|
|
81
90
|
exports: package.spec.exports.keys,
|
|
82
91
|
signer: signer,
|
|
83
92
|
signer_fingerprint: signer_fingerprint,
|
|
84
|
-
published_at: Time.now.
|
|
93
|
+
published_at: Time.now.to_i,
|
|
85
94
|
signature: Registry.registry_artifact_signature(archive_bytes, package.tree_hash, signer)
|
|
86
95
|
)
|
|
87
96
|
end
|
|
88
97
|
|
|
98
|
+
def stored_package_artifact(root, artifact_path, package, existing)
|
|
99
|
+
return unless existing&.artifact == artifact_path && existing.tree_hash == package.tree_hash
|
|
100
|
+
|
|
101
|
+
stored_path = File.join(root, artifact_path)
|
|
102
|
+
return unless File.file?(stored_path)
|
|
103
|
+
|
|
104
|
+
artifact_bytes = File.binread(stored_path)
|
|
105
|
+
return unless existing.artifact_hash == Hashing.sha256_prefixed(artifact_bytes)
|
|
106
|
+
return unless stored_prayspec_matches?(artifact_bytes, package.root)
|
|
107
|
+
|
|
108
|
+
artifact_bytes
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def stored_publish_matches?(artifact_bytes, package, signer, signer_fingerprint, existing)
|
|
112
|
+
existing.signer == signer &&
|
|
113
|
+
existing.signer_fingerprint == signer_fingerprint &&
|
|
114
|
+
existing.signature == Registry.registry_artifact_signature(artifact_bytes, package.tree_hash, signer)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def stored_prayspec_matches?(artifact_bytes, package_root)
|
|
118
|
+
Dir.mktmpdir("pray-publish-check-") do |directory|
|
|
119
|
+
Archive.unpack_praypkg(artifact_bytes, directory)
|
|
120
|
+
stored_path = Resolve.find_prayspec_file(directory)
|
|
121
|
+
current_path = Resolve.find_prayspec_file(package_root)
|
|
122
|
+
File.basename(stored_path) == File.basename(current_path) &&
|
|
123
|
+
File.binread(stored_path) == File.binread(current_path)
|
|
124
|
+
end
|
|
125
|
+
rescue Error, SystemCallError
|
|
126
|
+
false
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def preserve_existing_publish_metadata(metadata, version_entry, stored_artifact)
|
|
130
|
+
existing = metadata.versions.find { |entry| entry.version == version_entry.version }
|
|
131
|
+
return unless existing
|
|
132
|
+
|
|
133
|
+
version_entry.yanked = existing.yanked
|
|
134
|
+
version_entry.published_at = existing.published_at if stored_artifact
|
|
135
|
+
end
|
|
136
|
+
|
|
89
137
|
def load_registry_index(root)
|
|
90
138
|
path = File.join(root, "v1", "index.json")
|
|
91
139
|
return RegistryIndex.new unless File.exist?(path)
|
data/lib/pray/registry.rb
CHANGED
|
@@ -5,6 +5,7 @@ require "net/http"
|
|
|
5
5
|
require "uri"
|
|
6
6
|
require "fileutils"
|
|
7
7
|
require "pathname"
|
|
8
|
+
require "time"
|
|
8
9
|
require_relative "path_safety"
|
|
9
10
|
require_relative "http_body"
|
|
10
11
|
|
|
@@ -145,11 +146,26 @@ module Pray
|
|
|
145
146
|
exports: entry["exports"] || [],
|
|
146
147
|
signer: entry["signer"],
|
|
147
148
|
signer_fingerprint: entry["signer_fingerprint"],
|
|
148
|
-
published_at: entry["published_at"],
|
|
149
|
+
published_at: entry.key?("published_at") ? publish_timestamp_from_value(entry["published_at"]) : nil,
|
|
149
150
|
signature: entry["signature"]
|
|
150
151
|
)
|
|
151
152
|
end
|
|
152
153
|
|
|
154
|
+
def publish_timestamp_from_value(value)
|
|
155
|
+
timestamp = case value
|
|
156
|
+
when Integer then value
|
|
157
|
+
when String
|
|
158
|
+
value.match?(/\A\d+\z/) ? value.to_i : Time.iso8601(value).to_i
|
|
159
|
+
end
|
|
160
|
+
unless timestamp&.between?(0, 253_402_300_799)
|
|
161
|
+
raise Error.integrity("registry published_at must be whole UTC Unix seconds")
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
timestamp
|
|
165
|
+
rescue ArgumentError
|
|
166
|
+
raise Error.integrity("registry published_at must be whole UTC Unix seconds")
|
|
167
|
+
end
|
|
168
|
+
|
|
153
169
|
def select_package_version(metadata, constraint, preferred_version)
|
|
154
170
|
if preferred_version
|
|
155
171
|
version = metadata.versions.find { |entry| entry.version == preferred_version && !entry.yanked }
|
data/lib/pray/render_content.rb
CHANGED
|
@@ -34,7 +34,7 @@ module Pray
|
|
|
34
34
|
)
|
|
35
35
|
next if metadata.versions.empty?
|
|
36
36
|
|
|
37
|
-
updated_at = metadata.versions.
|
|
37
|
+
updated_at = (metadata.versions.filter_map(&:published_at).max || 0).to_s
|
|
38
38
|
{
|
|
39
39
|
"name" => name,
|
|
40
40
|
"updated_at" => updated_at,
|
|
@@ -57,7 +57,7 @@ module Pray
|
|
|
57
57
|
metadata = Publish.load_registry_package_metadata(metadata_path, name)
|
|
58
58
|
body = {
|
|
59
59
|
"name" => metadata.name,
|
|
60
|
-
"updated_at" => metadata.versions.
|
|
60
|
+
"updated_at" => (metadata.versions.filter_map(&:published_at).max || 0).to_s,
|
|
61
61
|
"versions" => metadata.versions.map { |version| transport_version(version) }
|
|
62
62
|
}
|
|
63
63
|
Serve.ok_response("application/json", JSON.pretty_generate(body))
|
|
@@ -78,9 +78,9 @@ module Pray
|
|
|
78
78
|
"tree_hash" => version.tree_hash.to_s,
|
|
79
79
|
"yanked" => version.yanked,
|
|
80
80
|
"targets" => version.targets,
|
|
81
|
-
"exports" => version.exports
|
|
82
|
-
"published_at" => version.published_at.to_s
|
|
81
|
+
"exports" => version.exports
|
|
83
82
|
}
|
|
83
|
+
hash["published_at"] = version.published_at if version.published_at
|
|
84
84
|
if version.signer || version.signer_fingerprint
|
|
85
85
|
hash["publisher"] = {
|
|
86
86
|
"id" => version.signer.to_s,
|
data/lib/pray/version.rb
CHANGED