44from pulp_glue .common .context import PluginRequirement , PulpEntityContext
55from pulp_glue .common .i18n import get_translation
66from pulp_glue .core .context import PulpArtifactContext
7- from pulp_glue .python .context import PulpPythonContentContext , PulpPythonRepositoryContext
7+ from pulp_glue .python .context import (
8+ PulpPythonContentContext ,
9+ PulpPythonProvenanceContext ,
10+ PulpPythonRepositoryContext ,
11+ )
812
913from pulp_cli .generic import (
1014 PulpCLIContext ,
1418 label_command ,
1519 label_select_option ,
1620 list_command ,
21+ load_json_callback ,
1722 pass_entity_context ,
1823 pass_pulp_context ,
1924 pulp_group ,
@@ -37,6 +42,24 @@ def _sha256_artifact_callback(
3742 return value
3843
3944
45+ def _attestation_callback (
46+ ctx : click .Context , param : click .Parameter , value : t .Iterable [str ] | None
47+ ) -> list [t .Any ] | None :
48+ """Callback to process multiple attestation values and combine them into a list."""
49+ if not value :
50+ return None
51+ result = []
52+ for attestation_value in value :
53+ # Use load_json_callback to process each value (supports JSON strings and file paths)
54+ processed = load_json_callback (ctx , param , attestation_value )
55+ # If it's already a list, extend; otherwise append
56+ if isinstance (processed , list ):
57+ result .extend (processed )
58+ else :
59+ result .append (processed )
60+ return result
61+
62+
4063repository_option = resource_option (
4164 "--repository" ,
4265 default_plugin = "python" ,
@@ -51,26 +74,44 @@ def _sha256_artifact_callback(
5174 ),
5275)
5376
77+ package_option = resource_option (
78+ "--package" ,
79+ default_plugin = "python" ,
80+ default_type = "package" ,
81+ lookup_key = "sha256" ,
82+ context_table = {
83+ "python:package" : PulpPythonContentContext ,
84+ },
85+ href_pattern = PulpPythonContentContext .HREF_PATTERN ,
86+ help = _ (
87+ "Package to associate the provenance with in the form '[[<plugin>:]<resource_type>:]<sha256>' or by href/prn."
88+ ),
89+ allowed_with_contexts = (PulpPythonProvenanceContext ,),
90+ required = True ,
91+ )
92+
5493
5594@pulp_group ()
5695@click .option (
5796 "-t" ,
5897 "--type" ,
5998 "content_type" ,
60- type = click .Choice (["package" ], case_sensitive = False ),
99+ type = click .Choice (["package" , "provenance" ], case_sensitive = False ),
61100 default = "package" ,
62101)
63102@pass_pulp_context
64103@click .pass_context
65104def content (ctx : click .Context , pulp_ctx : PulpCLIContext , / , content_type : str ) -> None :
66105 if content_type == "package" :
67106 ctx .obj = PulpPythonContentContext (pulp_ctx )
107+ elif content_type == "provenance" :
108+ ctx .obj = PulpPythonProvenanceContext (pulp_ctx )
68109 else :
69110 raise NotImplementedError ()
70111
71112
72113create_options = [
73- click . option ("--relative-path" , required = True , help = _ ("Exact name of file" )),
114+ pulp_option ("--relative-path" , required = True , help = _ ("Exact name of file" ), allowed_with_contexts = ( PulpPythonContentContext , )),
74115 click .option (
75116 "--sha256" ,
76117 "artifact" ,
@@ -79,21 +120,43 @@ def content(ctx: click.Context, pulp_ctx: PulpCLIContext, /, content_type: str)
79120 ),
80121 pulp_option (
81122 "--file-url" ,
82- help = _ ("Remote url to download and create python content from" ),
123+ help = _ ("Remote url to download and create {entity} from" ),
83124 needs_plugins = [PluginRequirement ("core" , specifier = ">=3.56.1" )],
84125 ),
126+ pulp_option (
127+ "--attestation" ,
128+ "attestations" ,
129+ multiple = True ,
130+ callback = _attestation_callback ,
131+ needs_plugins = [PluginRequirement ("python" , specifier = ">=3.22.0" )],
132+ help = _ (
133+ "A JSON object containing an attestation for the package. Can be a JSON string or a file path prefixed with '@'. Can be specified multiple times."
134+ ),
135+ allowed_with_contexts = (PulpPythonContentContext ,),
136+ ),
137+ ]
138+ provenance_create_options = [
139+ pulp_option ("--file" , type = click .File ("rb" ), help = _ ("Provenance JSON file" ), allowed_with_contexts = (PulpPythonProvenanceContext ,)),
140+ package_option ,
141+ pulp_option (
142+ "--verify/--no-verify" ,
143+ default = True ,
144+ needs_plugins = [PluginRequirement ("python" , specifier = ">=3.22.0" )],
145+ help = _ ("Verify the provenance" ),
146+ allowed_with_contexts = (PulpPythonProvenanceContext ,),
147+ ),
85148]
86149lookup_options = [href_option ]
87150content .add_command (
88151 list_command (
89152 decorators = [
90- click . option ("--filename" , type = str ),
153+ pulp_option ("--filename" , type = str , allowed_with_contexts = ( PulpPythonContentContext ,) ),
91154 label_select_option ,
92155 ]
93156 )
94157)
95158content .add_command (show_command (decorators = lookup_options ))
96- content .add_command (create_command (decorators = create_options ))
159+ content .add_command (create_command (decorators = create_options + provenance_create_options ))
97160content .add_command (
98161 label_command (
99162 decorators = lookup_options ,
@@ -102,10 +165,20 @@ def content(ctx: click.Context, pulp_ctx: PulpCLIContext, /, content_type: str)
102165)
103166
104167
105- @content .command ()
168+ @content .command (allowed_with_contexts = ( PulpPythonContentContext ,) )
106169@click .option ("--relative-path" , required = True , help = _ ("Exact name of file" ))
107170@click .option ("--file" , type = click .File ("rb" ), required = True , help = _ ("Path to file" ))
108171@chunk_size_option
172+ @pulp_option (
173+ "--attestation" ,
174+ "attestations" ,
175+ multiple = True ,
176+ callback = _attestation_callback ,
177+ needs_plugins = [PluginRequirement ("python" , specifier = ">=3.22.0" )],
178+ help = _ (
179+ "A JSON object containing an attestation for the package. Can be a JSON string or a file path prefixed with '@'. Can be specified multiple times."
180+ ),
181+ )
109182@repository_option
110183@pass_entity_context
111184@pass_pulp_context
@@ -116,12 +189,17 @@ def upload(
116189 relative_path : str ,
117190 file : t .IO [bytes ],
118191 chunk_size : int ,
192+ attestations : list [t .Any ] | None ,
119193 repository : PulpPythonRepositoryContext | None ,
120194) -> None :
121195 """Create a Python package content unit through uploading a file"""
122196 assert isinstance (entity_ctx , PulpPythonContentContext )
123197
124198 result = entity_ctx .upload (
125- relative_path = relative_path , file = file , chunk_size = chunk_size , repository = repository
199+ relative_path = relative_path ,
200+ file = file ,
201+ chunk_size = chunk_size ,
202+ repository = repository ,
203+ attestations = attestations ,
126204 )
127205 pulp_ctx .output_result (result )
0 commit comments