Skip to content

Commit e7a6f71

Browse files
committed
add commented example config output
1 parent 19a3369 commit e7a6f71

File tree

4 files changed

+111
-24
lines changed

4 files changed

+111
-24
lines changed

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ clap = { version = "4.5.17", features = ["derive", "string"] }
1111
clap-verbosity-flag = "2.2.1"
1212
colored = "2.1.0"
1313
dirs = "5.0.1"
14+
documented = "0.8.0"
1415
futures = "0.3.30"
1516
gitlab = "0.1705.0"
1617
http = "1.1.0"
@@ -21,6 +22,8 @@ serde_derive = "1.0.210"
2122
serde_json = "1.0.128"
2223
shellexpand = "3.1.0"
2324
simple_logger = { version = "5.0.0", features = ["stderr"] }
25+
struct-field-names-as-array = "0.3.0"
2426
tokio = { version = "1.40.0", features = ["full"] }
2527
tokio-util = "0.7.12"
2628
toml = "0.8.19"
29+
toml_edit = "0.22.22"

src/config.rs

+103-21
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
use anyhow::Context;
2+
use documented::DocumentedFields;
3+
use itertools::Itertools;
14
use log::warn;
25
use std::{
36
collections::HashMap,
47
fs::read_to_string,
58
io::Write,
69
path::{Path, PathBuf},
710
};
11+
use struct_field_names_as_array::FieldNamesAsArray;
12+
use toml_edit::{DocumentMut, RawString};
813

914
use serde::{de::Error, Deserializer, Serializer};
1015
use serde_derive::{Deserialize, Serialize};
@@ -84,19 +89,20 @@ impl<'de> serde::Deserialize<'de> for BoolOrString {
8489
}
8590
}
8691

87-
#[derive(Debug, Deserialize, Serialize)]
92+
#[derive(Debug, DocumentedFields, FieldNamesAsArray, Deserialize, Serialize)]
8893
pub struct GitLabRunnerInstance {
8994
/// Tags whose associated jobs will be run by this runner
9095
pub tags: Vec<String>,
9196
/// 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.
9398
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
96102
pub config_variables: HashMap<String, String>,
97103
}
98104

99-
#[derive(Debug, Deserialize, Serialize)]
105+
#[derive(Debug, DocumentedFields, FieldNamesAsArray, Deserialize, Serialize)]
100106
pub struct GitLabLaunchConfig {
101107
/// Executable name or path, will NOT be variable-expanded
102108
pub executable: String,
@@ -126,7 +132,7 @@ pub enum GitLabExecutorPullPolicy {
126132
Never,
127133
}
128134

129-
#[derive(Debug, Deserialize, Serialize)]
135+
#[derive(Debug, DocumentedFields, FieldNamesAsArray, Deserialize, Serialize)]
130136
pub struct GitLabCustomExecutorConfigTemplate {
131137
/// Override builds_dir provided by gitlab-runner config, will be variable-expanded
132138
pub builds_dir: Option<String>,
@@ -166,13 +172,13 @@ pub struct GitLabCustomExecutorConfig {
166172
pub cache_dir: PathBuf,
167173
}
168174

169-
#[derive(Debug, Deserialize, Serialize)]
175+
#[derive(Debug, DocumentedFields, FieldNamesAsArray, Deserialize, Serialize)]
170176
pub struct GitLabPollConfig {
171177
/// Interval (in seconds) for polling for new jobs
172178
pub interval: u32,
173179
}
174180

175-
#[derive(Debug, Deserialize, Serialize)]
181+
#[derive(Debug, DocumentedFields, FieldNamesAsArray, Deserialize, Serialize)]
176182
pub struct GitLabRunnersConfig {
177183
/// Unique name for the meta-runner
178184
pub name: String,
@@ -189,21 +195,38 @@ pub struct GitLabRunnersConfig {
189195
pub poll: GitLabPollConfig,
190196
/// Configuration for launching ephemeral runners
191197
/// 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
192205
pub launch: Option<GitLabLaunchConfig>,
193206
/// Configuration for the custom executor
194207
/// 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
195213
pub executor: Option<GitLabCustomExecutorConfigTemplate>,
196214
/// Configuration template for gitlab-runner config file
197215
/// It will be instantiated for every runner in the runners array,
198216
/// 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)`
199222
pub runner: gitlab_config::Runner,
200223
}
201224

202225
fn strs_to_strings(strs: &[&str]) -> Vec<String> {
203226
strs.iter().map(|&s| s.into()).collect()
204227
}
205228

206-
pub fn get_default_config() -> GitLabRunnersConfig {
229+
pub fn get_example_config() -> GitLabRunnersConfig {
207230
GitLabRunnersConfig {
208231
name: "meta-runner".into(),
209232
project: "gitlab-org/gitlab".into(),
@@ -224,24 +247,24 @@ pub fn get_default_config() -> GitLabRunnersConfig {
224247
cleanup_args: strs_to_strings(&["executor", "$NAME", "cleanup"]),
225248
},
226249
},
227-
environment: None,
250+
environment: Some(vec!["ENV_VARIABLE=value".into()]),
228251
},
229252
launch: Some(GitLabLaunchConfig {
230253
executable: "sbatch".into(),
231254
args: [].into_iter().map(str::to_string).collect(),
232-
timeout: None,
255+
timeout: Some(300),
233256
stdin: Some(
234257
"#!/bin/bash\ngitlab-runner run-single --config $CONFIG --runner $NAME --max-builds $NUM_JOBS --wait-timeout 1\n".into(),
235258
),
236-
workdir: None,
259+
workdir: Some("$HOME/launch".into()),
237260
group_size: 1,
238261
}),
239262
poll: GitLabPollConfig { interval: 30 },
240263
runners: [(
241264
"test-runner".to_owned(),
242265
GitLabRunnerInstance {
243266
tags: vec!["tag-1".to_owned(), "tag-2".to_owned()],
244-
launch_priority: None,
267+
launch_priority: Some(10),
245268
config_variables: [("VARIABLE", "value")]
246269
.map(|(k, v)| (k.to_owned(), v.to_owned()))
247270
.into_iter()
@@ -251,10 +274,10 @@ pub fn get_default_config() -> GitLabRunnersConfig {
251274
.into_iter()
252275
.collect(),
253276
executor: Some(GitLabCustomExecutorConfigTemplate {
254-
builds_dir: None,
277+
builds_dir: Some("$HOME/builds".into()),
255278
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()),
258281
pull_policy: GitLabExecutorPullPolicy::IfNotPresent,
259282
apptainer_executable: "apptainer".into(),
260283
gpu_amd: BoolOrString::Bool(false),
@@ -273,13 +296,72 @@ pub fn read_config(filename: &Path) -> anyhow::Result<GitLabRunnersConfig> {
273296
Ok(parsed)
274297
}
275298

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()
278359
}
279360

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())?;
283365
Ok(())
284366
}
285367

src/gitlab_config.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
use documented::DocumentedFields;
12
use serde_derive::{Deserialize, Serialize};
3+
use struct_field_names_as_array::FieldNamesAsArray;
24

3-
#[derive(Debug, Deserialize, Serialize)]
5+
#[derive(Debug, DocumentedFields, FieldNamesAsArray, Deserialize, Serialize)]
46
pub struct Runner {
57
/// Directory to use for builds, will be variable-expanded
68
pub builds_dir: String,
@@ -13,7 +15,7 @@ pub struct Runner {
1315
pub environment: Option<Vec<String>>,
1416
}
1517

16-
#[derive(Debug, Deserialize, Serialize)]
18+
#[derive(Debug, DocumentedFields, FieldNamesAsArray, Deserialize, Serialize)]
1719
pub struct CustomExecutor {
1820
/// The executable to configure a job, will be template-expanded
1921
pub config_exec: String,

src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ fn main() -> anyhow::Result<()> {
2626
.init()
2727
.unwrap();
2828
match cli.command {
29-
cli::Command::CreateExampleConfig => config::write_default_config(&cli.paths.config_file),
29+
cli::Command::CreateExampleConfig => config::write_example_config(&cli.paths.config_file),
3030
cli::Command::ShowExampleConfig => Ok(println!("{}", config::get_default_config_str())),
3131
cli::Command::CheckConfig => check_config::check(&cli.paths),
3232
cli::Command::ShowConfig => check_config::show(&cli.paths),

0 commit comments

Comments
 (0)