1- use anyhow:: { Context , Result , anyhow} ;
2- use base64:: Engine ;
1+ use anyhow:: Result ;
32use clap:: Parser ;
4- use glob:: glob;
5- use serde_yaml:: Value ;
6- use std:: fs;
7- use std:: io:: Write ;
8- use std:: path:: Path ;
9- use std:: process:: { Command , Stdio } ;
10-
11- #[ derive( Parser , Debug ) ]
12- struct Args {
13- /// Base64-encoded private GPG key
14- #[ clap( long) ]
3+ use dotenv:: dotenv;
4+ use sops_gitops_github_action:: import_gpg_key;
5+ use sops_gitops_github_action:: update_sops_config;
6+
7+ /// CLI arguments for the sops-gitops-github-action
8+ #[ derive( Debug , Parser ) ]
9+ #[ command( name = "sops-gitops-github-action1" ) ]
10+ struct MyArgs {
11+ /// The base64-encoded private GPG key
12+ #[ arg( long) ]
1513 private_key : String ,
16- /// Comma-separated list of base64-encoded public GPG keys
17- #[ clap( long) ]
14+
15+ /// A comma-separated list of base64-encoded public GPG keys
16+ #[ arg( long) ]
1817 public_keys : String ,
1918}
2019
2120#[ tokio:: main]
2221async fn main ( ) -> Result < ( ) > {
23- let args = Args :: parse ( ) ;
22+ dotenv ( ) . ok ( ) ;
23+ let args = MyArgs :: parse ( ) ;
2424
2525 // Step 1: Decode and import the private key
2626 println ! ( "Importing private key..." ) ;
@@ -40,202 +40,3 @@ async fn main() -> Result<()> {
4040 println ! ( "Action completed!" ) ;
4141 Ok ( ( ) )
4242}
43-
44- fn import_gpg_key ( encoded_key : & str ) -> Result < ( ) , anyhow:: Error > {
45- let decoded_key = base64:: engine:: general_purpose:: STANDARD
46- . decode ( encoded_key)
47- . context ( "Failed to decode base64 key" ) ?;
48- let mut process = Command :: new ( "gpg" )
49- . args ( [ "--import" ] )
50- . stdin ( Stdio :: piped ( ) )
51- . spawn ( )
52- . context ( "Failed to spawn gpg process" ) ?;
53-
54- if let Some ( stdin) = process. stdin . as_mut ( ) {
55- stdin. write_all ( & decoded_key) ?;
56- }
57- process. wait ( ) . context ( "Failed to wait for gpg process" ) ?;
58- Ok ( ( ) )
59- }
60-
61- fn update_sops_config ( public_keys : & str ) -> Result < ( ) , anyhow:: Error > {
62- let sops_config_path = "./.sops.yaml" ;
63- let mut config: Value = if Path :: new ( sops_config_path) . exists ( ) {
64- let content =
65- fs:: read_to_string ( sops_config_path) . context ( "Failed to read existing .sops.yaml" ) ?;
66- serde_yaml:: from_str ( & content) . context ( "Failed to parse .sops.yaml" ) ?
67- } else {
68- serde_yaml:: from_str ( "creation_rules: []" ) . unwrap ( )
69- } ;
70-
71- // Update the creation rules with public keys
72- let creation_rules = config
73- . get_mut ( "creation_rules" )
74- . and_then ( Value :: as_sequence_mut)
75- . ok_or_else ( || anyhow ! ( "Invalid .sops.yaml structure" ) ) ?;
76-
77- for public_key in public_keys. split ( ',' ) {
78- let fingerprint = get_key_fingerprint ( public_key) ?;
79- let rule = serde_yaml:: to_value ( & serde_yaml:: Mapping :: from_iter ( [ (
80- Value :: String ( "pgp" . to_string ( ) ) ,
81- Value :: Sequence ( vec ! [ Value :: String ( fingerprint) ] ) ,
82- ) ] ) ) ?;
83- creation_rules. push ( rule) ;
84- }
85-
86- // Write back the updated configuration
87- let updated_config =
88- serde_yaml:: to_string ( & config) . context ( "Failed to serialize updated .sops.yaml" ) ?;
89- fs:: write ( sops_config_path, updated_config) . context ( "Failed to write updated .sops.yaml" ) ?;
90- Ok ( ( ) )
91- }
92-
93- fn get_key_fingerprint ( encoded_key : & str ) -> Result < String , anyhow:: Error > {
94- let _decoded_key = base64:: engine:: general_purpose:: STANDARD
95- . decode ( encoded_key)
96- . context ( "Failed to decode base64 key" ) ?;
97- let output = Command :: new ( "gpg" )
98- . args ( [
99- "--with-colons" ,
100- "--import-options" ,
101- "show-only" ,
102- "--import" ,
103- "--fingerprint" ,
104- ] )
105- . stdin ( Stdio :: piped ( ) )
106- . stdout ( Stdio :: piped ( ) )
107- . spawn ( )
108- . context ( "Failed to spawn gpg process" ) ?
109- . wait_with_output ( )
110- . context ( "Failed to wait for gpg process" ) ?;
111-
112- let output_str = String :: from_utf8 ( output. stdout ) . context ( "Failed to parse gpg output" ) ?;
113- let fingerprint = output_str
114- . lines ( )
115- . find ( |line| line. starts_with ( "fpr" ) )
116- . and_then ( |line| line. split ( ':' ) . nth ( 9 ) )
117- . map ( String :: from)
118- . context ( "Failed to extract fingerprint from gpg output" ) ?;
119- Ok ( fingerprint)
120- }
121-
122- #[ allow( unused) ]
123- fn setup_workspace ( ) -> Result < ( ) , anyhow:: Error > {
124- println ! ( "Setting up workspace..." ) ;
125- fs:: copy ( "/generator/." , "." ) ?;
126- Ok ( ( ) )
127- }
128-
129- #[ allow( unused) ]
130- fn debug_output ( ) -> Result < ( ) , anyhow:: Error > {
131- println ! ( "Working dir: {}" , std:: env:: current_dir( ) ?. display( ) ) ;
132- println ! ( "================================================" ) ;
133- for entry in fs:: read_dir ( "." ) ? {
134- let entry = entry?;
135- println ! ( "{:?}" , entry. path( ) ) ;
136- }
137- println ! ( "================================================" ) ;
138- Ok ( ( ) )
139- }
140-
141- #[ allow( unused) ]
142- fn sops_config_file_exists ( ) -> Result < bool , anyhow:: Error > {
143- let path = Path :: new ( "actions/generator/workspace/.sops.yaml" ) ;
144- Ok ( path. exists ( ) )
145- }
146-
147- #[ allow( unused) ]
148- fn create_default_sops_config_file ( ) -> Result < ( ) , anyhow:: Error > {
149- println ! ( "Creating .sops.yaml..." ) ;
150- let team_key_fpr = get_key_fingerprint ( "team_private_key" ) ?;
151- let content = format ! (
152- "creation_rules:\n - key_groups:\n - pgp:\n - {}" ,
153- team_key_fpr
154- ) ;
155- fs:: write ( "actions/generator/workspace/.sops.yaml" , content) ?;
156- Ok ( ( ) )
157- }
158-
159- #[ allow( unused) ]
160- fn public_keys_provided ( ) -> Result < bool , anyhow:: Error > {
161- let public_keys_file = "actions/generator/public_keys.txt" ;
162- let content = fs:: read_to_string ( public_keys_file) . unwrap_or_default ( ) ;
163- let public_keys: Vec < & str > = content. lines ( ) . collect ( ) ;
164- Ok ( !public_keys. is_empty ( ) )
165- }
166-
167- #[ allow( unused) ]
168- fn update_sops_configuration_file ( ) -> Result < ( ) , anyhow:: Error > {
169- println ! ( "Updating .sops.yaml file with public keys..." ) ;
170- let public_keys_file = "actions/generator/public_keys.txt" ;
171- let binding = fs:: read_to_string ( public_keys_file) ?;
172- let public_keys = binding. lines ( ) . collect :: < Vec < _ > > ( ) ;
173-
174- let mut config = fs:: read_to_string ( "actions/generator/workspace/.sops.yaml" ) ?;
175- for key in public_keys {
176- let key_fingerprint = get_key_fingerprint ( key) ?;
177- config. push_str ( & format ! ( "\n - {}" , key_fingerprint) ) ;
178- }
179- fs:: write ( "actions/generator/workspace/.sops.yaml" , config) ?;
180- Ok ( ( ) )
181- }
182-
183- #[ allow( unused) ]
184- fn find_secret_files ( workspace : & str ) -> Result < Vec < String > , anyhow:: Error > {
185- let mut secret_files = Vec :: new ( ) ;
186- for entry in glob ( & format ! ( "{}/**/*.yaml" , workspace) ) ? {
187- let path = entry?;
188- let content = fs:: read_to_string ( & path) ?;
189- if content. contains ( "sops:" ) {
190- secret_files. push ( path. to_string_lossy ( ) . to_string ( ) ) ;
191- }
192- }
193- Ok ( secret_files)
194- }
195-
196- #[ allow( unused) ]
197- fn update_secret_file ( file_path : & str ) -> Result < ( ) , anyhow:: Error > {
198- println ! ( "Updating secret file: {}" , file_path) ;
199- if Path :: new ( file_path) . exists ( ) {
200- println ! ( "The secret file {} exists." , file_path) ;
201- Command :: new ( "sops" )
202- . args ( [ "updatekeys" , file_path, "--yes" ] )
203- . stdout ( Stdio :: inherit ( ) )
204- . stderr ( Stdio :: inherit ( ) )
205- . output ( )
206- . with_context ( || format ! ( "Failed to re-encrypt secret file: {}" , file_path) ) ?;
207- } else {
208- println ! ( "Creating secret file: {}" , file_path) ;
209- create_secret_file ( file_path) ?;
210- }
211- Ok ( ( ) )
212- }
213-
214- #[ allow( unused) ]
215- fn create_secret_file ( file_path : & str ) -> Result < ( ) , anyhow:: Error > {
216- fs:: create_dir_all ( Path :: new ( file_path) . parent ( ) . unwrap ( ) ) ?;
217- let sops_config_path = "actions/generator/workspace/.sops.yaml" ;
218-
219- let mut child = Command :: new ( "sops" )
220- . args ( [ "--config" , sops_config_path, "-e" , "/dev/stdin" ] )
221- . stdin ( Stdio :: piped ( ) )
222- . stdout ( Stdio :: from ( fs:: File :: create ( file_path) ?) )
223- . spawn ( )
224- . context ( "Failed to spawn sops process" ) ?;
225-
226- if let Some ( stdin) = child. stdin . as_mut ( ) {
227- writeln ! ( stdin, "key: value" ) . context ( "Failed to write to stdin" ) ?;
228- } else {
229- return Err ( anyhow:: anyhow!( "Failed to open stdin for sops process" ) ) ;
230- }
231-
232- child. wait ( ) . context ( "Failed to wait for sops process" ) ?;
233- Ok ( ( ) )
234- }
235-
236- #[ allow( unused) ]
237- fn set_message ( ) -> Result < ( ) , anyhow:: Error > {
238- let message = "encrypt sops secrets and update sops.yaml" ;
239- println ! ( "message={}" , message) ;
240- Ok ( ( ) )
241- }
0 commit comments