github_workflows_update/
cmd.rs

1// Copyright (C) 2022 Leandro Lisboa Penz <lpenz@lpenz.org>
2// This file is subject to the terms and conditions defined in
3// file 'LICENSE', which is part of this source code package.
4
5//! Command line arguments parsing and main function.
6
7use anyhow::Result;
8use futures::future::join_all;
9use tokio_stream::StreamExt;
10use tokio_stream::wrappers::ReadDirStream;
11use tracing::Level;
12use tracing::event;
13
14use clap::Parser;
15use clap::ValueEnum;
16
17use crate::proxy;
18
19#[derive(Parser, Debug)]
20#[command(author, version, about, long_about = None)]
21pub struct Args {
22    /// Don't update the workflows, just print what would be done
23    #[clap(short = 'n', long = "dry-run")]
24    pub dryrun: bool,
25    /// Output format for the outdated action messages
26    #[clap(short = 'f', long, value_enum, value_parser)]
27    pub output_format: Option<OutputFormat>,
28    /// Return error if any outdated actions are found
29    #[clap(long)]
30    pub error_on_outdated: bool,
31}
32
33#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Default, Debug)]
34pub enum OutputFormat {
35    #[default]
36    Standard,
37    /// Generate messages as github action warnings
38    GithubWarning,
39}
40
41#[tokio::main]
42pub async fn main() -> Result<()> {
43    let args = Args::parse();
44    env_logger::init();
45    let proxy_server = proxy::Server::new();
46    let futures = ReadDirStream::new(tokio::fs::read_dir(".github/workflows").await?)
47        .filter_map(|filename| match filename {
48            Ok(filename) => Some(filename.path()),
49            Err(ref e) => {
50                event!(
51                    Level::ERROR,
52                    error = ?e,
53                    filename = ?filename,
54                    "error getting filename from .github/workflows"
55                );
56                None
57            }
58        })
59        .map(|f| {
60            crate::processor::process_file(
61                args.dryrun,
62                args.output_format.unwrap_or_default(),
63                &proxy_server,
64                f,
65            )
66        })
67        .collect::<Vec<_>>()
68        .await;
69    let mut any_outdated = false;
70    for result in join_all(futures).await {
71        match result {
72            Ok(true) => {
73                any_outdated = true;
74            }
75            Err(_) => {
76                // Errors are traced by the underlying functions, we
77                // just need to report the failure to the shell
78                std::process::exit(1);
79            }
80            _ => {}
81        }
82    }
83    if any_outdated && args.error_on_outdated {
84        match args.output_format.unwrap_or_default() {
85            OutputFormat::Standard => {
86                eprintln!("Found oudated entities");
87            }
88            OutputFormat::GithubWarning => {
89                println!("::error ::outdated entities found");
90            }
91        }
92        std::process::exit(2);
93    }
94    Ok(())
95}