1
+ use anyhow:: Context ;
2
+ use documented:: DocumentedFields ;
3
+ use itertools:: Itertools ;
1
4
use log:: warn;
2
5
use std:: {
3
6
collections:: HashMap ,
4
7
fs:: read_to_string,
5
8
io:: Write ,
6
9
path:: { Path , PathBuf } ,
7
10
} ;
11
+ use struct_field_names_as_array:: FieldNamesAsArray ;
12
+ use toml_edit:: { DocumentMut , RawString } ;
8
13
9
14
use serde:: { de:: Error , Deserializer , Serializer } ;
10
15
use serde_derive:: { Deserialize , Serialize } ;
@@ -84,19 +89,20 @@ impl<'de> serde::Deserialize<'de> for BoolOrString {
84
89
}
85
90
}
86
91
87
- #[ derive( Debug , Deserialize , Serialize ) ]
92
+ #[ derive( Debug , DocumentedFields , FieldNamesAsArray , Deserialize , Serialize ) ]
88
93
pub struct GitLabRunnerInstance {
89
94
/// Tags whose associated jobs will be run by this runner
90
95
pub tags : Vec < String > ,
91
96
/// Priority in which the instances' launch processes should be executed, higher priority means earlier launch.
92
- /// All jobs without a priority will be launched last
97
+ /// All jobs without a priority will be launched last.
93
98
pub launch_priority : Option < u32 > ,
94
- /// Variables to be expanded in the template instantiation
95
- /// Naming to avoid confusing with environment variables
99
+ /// Variables to be expanded in the template instantiation.
100
+ /// Each value needs to be a string!
101
+ // Naming to avoid confusing with environment variables
96
102
pub config_variables : HashMap < String , String > ,
97
103
}
98
104
99
- #[ derive( Debug , Deserialize , Serialize ) ]
105
+ #[ derive( Debug , DocumentedFields , FieldNamesAsArray , Deserialize , Serialize ) ]
100
106
pub struct GitLabLaunchConfig {
101
107
/// Executable name or path, will NOT be variable-expanded
102
108
pub executable : String ,
@@ -126,7 +132,7 @@ pub enum GitLabExecutorPullPolicy {
126
132
Never ,
127
133
}
128
134
129
- #[ derive( Debug , Deserialize , Serialize ) ]
135
+ #[ derive( Debug , DocumentedFields , FieldNamesAsArray , Deserialize , Serialize ) ]
130
136
pub struct GitLabCustomExecutorConfigTemplate {
131
137
/// Override builds_dir provided by gitlab-runner config, will be variable-expanded
132
138
pub builds_dir : Option < String > ,
@@ -166,13 +172,13 @@ pub struct GitLabCustomExecutorConfig {
166
172
pub cache_dir : PathBuf ,
167
173
}
168
174
169
- #[ derive( Debug , Deserialize , Serialize ) ]
175
+ #[ derive( Debug , DocumentedFields , FieldNamesAsArray , Deserialize , Serialize ) ]
170
176
pub struct GitLabPollConfig {
171
177
/// Interval (in seconds) for polling for new jobs
172
178
pub interval : u32 ,
173
179
}
174
180
175
- #[ derive( Debug , Deserialize , Serialize ) ]
181
+ #[ derive( Debug , DocumentedFields , FieldNamesAsArray , Deserialize , Serialize ) ]
176
182
pub struct GitLabRunnersConfig {
177
183
/// Unique name for the meta-runner
178
184
pub name : String ,
@@ -189,21 +195,38 @@ pub struct GitLabRunnersConfig {
189
195
pub poll : GitLabPollConfig ,
190
196
/// Configuration for launching ephemeral runners
191
197
/// Some of the configuration variables allow variable expansion from the runner instance variables
198
+ /// Available variables are (in order of precedence)
199
+ /// - $NAME for the runner instance name, to be passed to `gitlab-runner run-single --runner-name $NAME``
200
+ /// - $THIS for the path to this executable
201
+ /// - $CONFIG for the path to the generated gitlab-runner config file, to be passed to `gitlab-runner --config $CONFIG`
202
+ /// - $NUM_JOBS for the number of jobs that were grouped together for this launch, to be passed to `gitlab-runner run-single --max-builds 1`
203
+ /// - Any variables defined in runners.<runner_name>.config_variables
204
+ /// - Any environment variables provided by gitlab-runner to this custom executor
192
205
pub launch : Option < GitLabLaunchConfig > ,
193
206
/// Configuration for the custom executor
194
207
/// Some of the configuration variables allow variable expansion from the runner instance variables
208
+ /// Available variables are (in order of precedence)
209
+ /// - $NAME for the runner instance name
210
+ /// - $THIS for the path to this executable
211
+ /// - Any variables defined in runners.<runner_name>.config_variables
212
+ /// - Any environment variables provided by gitlab-runner to this custom executor
195
213
pub executor : Option < GitLabCustomExecutorConfigTemplate > ,
196
214
/// Configuration template for gitlab-runner config file
197
215
/// It will be instantiated for every runner in the runners array,
198
216
/// expanding occurrences of the runner instance variables into their values
217
+ /// Available variables are (in order of precedence)
218
+ /// - $NAME for the runner instance name
219
+ /// - $THIS for the path to this executable
220
+ /// - Any variables defined in runners.<runner_name>.config_variables
221
+ /// - Any environment variables available when calling `gitlab-meta-runner (configure|show-config)`
199
222
pub runner : gitlab_config:: Runner ,
200
223
}
201
224
202
225
fn strs_to_strings ( strs : & [ & str ] ) -> Vec < String > {
203
226
strs. iter ( ) . map ( |& s| s. into ( ) ) . collect ( )
204
227
}
205
228
206
- pub fn get_default_config ( ) -> GitLabRunnersConfig {
229
+ pub fn get_example_config ( ) -> GitLabRunnersConfig {
207
230
GitLabRunnersConfig {
208
231
name : "meta-runner" . into ( ) ,
209
232
project : "gitlab-org/gitlab" . into ( ) ,
@@ -224,24 +247,24 @@ pub fn get_default_config() -> GitLabRunnersConfig {
224
247
cleanup_args : strs_to_strings ( & [ "executor" , "$NAME" , "cleanup" ] ) ,
225
248
} ,
226
249
} ,
227
- environment : None ,
250
+ environment : Some ( vec ! [ "ENV_VARIABLE=value" . into ( ) ] ) ,
228
251
} ,
229
252
launch : Some ( GitLabLaunchConfig {
230
253
executable : "sbatch" . into ( ) ,
231
254
args : [ ] . into_iter ( ) . map ( str:: to_string) . collect ( ) ,
232
- timeout : None ,
255
+ timeout : Some ( 300 ) ,
233
256
stdin : Some (
234
257
"#!/bin/bash\n gitlab-runner run-single --config $CONFIG --runner $NAME --max-builds $NUM_JOBS --wait-timeout 1\n " . into ( ) ,
235
258
) ,
236
- workdir : None ,
259
+ workdir : Some ( "$HOME/launch" . into ( ) ) ,
237
260
group_size : 1 ,
238
261
} ) ,
239
262
poll : GitLabPollConfig { interval : 30 } ,
240
263
runners : [ (
241
264
"test-runner" . to_owned ( ) ,
242
265
GitLabRunnerInstance {
243
266
tags : vec ! [ "tag-1" . to_owned( ) , "tag-2" . to_owned( ) ] ,
244
- launch_priority : None ,
267
+ launch_priority : Some ( 10 ) ,
245
268
config_variables : [ ( "VARIABLE" , "value" ) ]
246
269
. map ( |( k, v) | ( k. to_owned ( ) , v. to_owned ( ) ) )
247
270
. into_iter ( )
@@ -251,10 +274,10 @@ pub fn get_default_config() -> GitLabRunnersConfig {
251
274
. into_iter ( )
252
275
. collect ( ) ,
253
276
executor : Some ( GitLabCustomExecutorConfigTemplate {
254
- builds_dir : None ,
277
+ builds_dir : Some ( "$HOME/builds" . into ( ) ) ,
255
278
image_dir : "$HOME/images" . into ( ) ,
256
- image_cache_dir : None ,
257
- image_tmp_dir : None ,
279
+ image_cache_dir : Some ( "$HOME/image_cache" . into ( ) ) ,
280
+ image_tmp_dir : Some ( "$HOME/image_tmp" . into ( ) ) ,
258
281
pull_policy : GitLabExecutorPullPolicy :: IfNotPresent ,
259
282
apptainer_executable : "apptainer" . into ( ) ,
260
283
gpu_amd : BoolOrString :: Bool ( false ) ,
@@ -273,13 +296,72 @@ pub fn read_config(filename: &Path) -> anyhow::Result<GitLabRunnersConfig> {
273
296
Ok ( parsed)
274
297
}
275
298
276
- pub fn get_default_config_str ( ) -> String {
277
- toml:: to_string_pretty ( & get_default_config ( ) ) . unwrap ( )
299
+ fn annotate_toml_table < T : DocumentedFields > ( table : & mut toml_edit:: Table ) {
300
+ for ( mut key, value) in table. iter_mut ( ) {
301
+ let key_name = key. get ( ) . to_owned ( ) ;
302
+ let comments = T :: get_field_docs ( key_name) . map_or ( "" . into ( ) , |comment| {
303
+ format ! ( "# {}\n " , comment. lines( ) . join( "\n # " ) )
304
+ } ) ;
305
+ match value {
306
+ toml_edit:: Item :: None => ( ) ,
307
+ toml_edit:: Item :: Value ( _) => {
308
+ key. leaf_decor_mut ( ) . set_prefix ( comments) ;
309
+ }
310
+ toml_edit:: Item :: Table ( table) => {
311
+ let original_decor = table
312
+ . decor ( )
313
+ . prefix ( )
314
+ . map_or ( RawString :: default ( ) , |v| v. to_owned ( ) ) ;
315
+ table. decor_mut ( ) . set_prefix ( format ! (
316
+ "{}{}" ,
317
+ original_decor. as_str( ) . unwrap_or( "" ) ,
318
+ comments
319
+ ) ) ;
320
+ }
321
+ // doesn't appear in our configuration
322
+ toml_edit:: Item :: ArrayOfTables ( _) => todo ! ( ) ,
323
+ } ;
324
+ }
325
+ }
326
+
327
+ pub fn get_example_config_str ( ) -> String {
328
+ let config = get_example_config ( ) ;
329
+ let mut document = toml:: to_string_pretty ( & config)
330
+ . unwrap ( )
331
+ . parse :: < DocumentMut > ( )
332
+ . unwrap ( ) ;
333
+ annotate_toml_table :: < GitLabRunnersConfig > ( document. as_table_mut ( ) ) ;
334
+ {
335
+ let runners = document. get_mut ( "runners" ) . unwrap ( ) ;
336
+ for ( name, _) in & config. runners {
337
+ annotate_toml_table :: < GitLabRunnerInstance > (
338
+ runners. get_mut ( name) . unwrap ( ) . as_table_mut ( ) . unwrap ( ) ,
339
+ ) ;
340
+ }
341
+ }
342
+ annotate_toml_table :: < GitLabPollConfig > (
343
+ document. get_mut ( "poll" ) . unwrap ( ) . as_table_mut ( ) . unwrap ( ) ,
344
+ ) ;
345
+ annotate_toml_table :: < GitLabLaunchConfig > (
346
+ document. get_mut ( "launch" ) . unwrap ( ) . as_table_mut ( ) . unwrap ( ) ,
347
+ ) ;
348
+ annotate_toml_table :: < GitLabCustomExecutorConfigTemplate > (
349
+ document
350
+ . get_mut ( "executor" )
351
+ . unwrap ( )
352
+ . as_table_mut ( )
353
+ . unwrap ( ) ,
354
+ ) ;
355
+ annotate_toml_table :: < gitlab_config:: Runner > (
356
+ document. get_mut ( "runner" ) . unwrap ( ) . as_table_mut ( ) . unwrap ( ) ,
357
+ ) ;
358
+ document. to_string ( )
278
359
}
279
360
280
- pub fn write_default_config ( filename : & Path ) -> anyhow:: Result < ( ) > {
281
- let mut file = std:: fs:: File :: create_new ( filename) ?;
282
- file. write_all ( get_default_config_str ( ) . as_bytes ( ) ) ?;
361
+ pub fn write_example_config ( filename : & Path ) -> anyhow:: Result < ( ) > {
362
+ let mut file = std:: fs:: File :: create_new ( filename)
363
+ . context ( format ! ( "Failed creating config file {:?}" , filename) ) ?;
364
+ file. write_all ( get_example_config_str ( ) . as_bytes ( ) ) ?;
283
365
Ok ( ( ) )
284
366
}
285
367
0 commit comments