You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: ci.rs
    path of file: ./repos/moon/crates/cli/src/commands
    
    the code of the file until where you have to start completion: use crate::app::GlobalArgs;
use crate::app_error::{AppError, ExitCode};
use crate::queries::touched_files::{query_touched_files, QueryTouchedFilesOptions};
use ci_env::CiOutput;
use clap::Args;
use itertools::Itertools;
use moon::{build_action_graph, generate_project_graph};
use moon_action_context::ActionContext;
use moon_action_graph::{ActionGraph, RunRequirements};
use moon_action_pipeline::Pipeline;
use moon_app_components::Console;
use moon_common::path::WorkspaceRelativePathBuf;
use moon_project_graph::ProjectGraph;
use moon_target::Target;
use moon_workspace::Workspace;
use rustc_hash::FxHashSet;
use starbase::{system, AppResult};
use starbase_styles::color;
use std::sync::Arc;
use tracing::debug;

type TargetList = Vec<Target>;

const HEADING_PARALLELISM: &str = "Parallelism and distribution";

#[derive(Args, Clone, Debug)]
pub struct CiArgs {
    #[arg(help = "List of targets (scope:task) to run")]
    targets: Vec<Target>,

    #[arg(long, help = "Base branch, commit, or revision to compare against")]
    base: Option<String>,

    #[arg(long, help = "Current branch, commit, or revision to compare with")]
    head: Option<String>,

    #[arg(long, help = "Index of the current job", help_heading = HEADING_PARALLELISM)]
    job: Option<usize>,

    #[arg(long = "jobTotal", help = "Total amount of jobs to run", help_heading = HEADING_PARALLELISM)]
    job_total: Option<usize>,
}

struct CiConsole<'ci> {
    inner: &'ci Console,
    output: CiOutput,
}

impl<'ci> CiConsole<'ci> {
    pub fn write_line<T: AsRef<[u8]>>(&self, data: T) -> miette::Result<()> {
        self.inner.out.write_line(data)
    }

    pub fn print_header(&self, title: &str) -> miette::Result<()> {
        self.write_line(format!("{}{}", self.output.open_log_group, ti
    
pub async fn ci(args: ArgsRef<CiArgs>, global_args: StateRef<GlobalArgs>, resources: ResourcesMut) {
    let project_graph = { generate_project_graph(resources.get_mut::<Workspace>()).await? };
    let workspace = resources.get::<Workspace>();
    let console = CiConsole {
        inner: resources.get::<Console>(),
        output: ci_env::get_output().unwrap_or(CiOutput {
            close_log_group: "",
            open_log_group: "▪▪▪▪ ",
        }),
    };

    let touched_files = gather_touched_files(&console, workspace, args).await?;
    let targets = gather_runnable_targets(&console, &project_graph, args)?;

    if targets.is_empty() {
        console.write_line(color::invalid("No targets to run"))?;

        return Ok(());
    }

    let targets = distribute_targets_across_jobs(&console, args, targets)?;
    let action_graph = generate_action_graph(&console, &project_graph, &targets, &touched_files)?;

    if action_graph.is_empty() {
        console.write_line(color::invalid("No targets to run based on touched files"))?;

        return Ok(());
    }

    // Process all tasks in the graph
    console.print_header("Running targets")?;

    let context = ActionContext {
        primary_targets: FxHashSet::from_iter(targets),
        touched_files,
        workspace_root: workspace.root.clone(),
        ..ActionContext::default()
    };

    let mut pipeline = Pipeline::new(workspace.to_owned(), project_graph);

    if let Some(concurrency) = &global_args.concurrency {
        pipeline.concurrency(*concurrency);
    }

    let results = pipeline
        .generate_report("ciReport.json")
        .run(
            action_graph,
            Arc::new(resources.get::<Console>().to_owned()),
            Some(context),
        )
        .await?;

    console.print_footer()?;

    // Print out a summary of any failures
    console.print_header("Summary")?;

    pipeline.render_summary(&results, console.inner)?;

    console.print_footer()?;

    // Print out the results and exit if an error occurs
    console.print_header("Stats")?;

    let failed = pipeline.render_results(&results, console.inner)?;

    pipeline.render_stats(&results, console.inner, false)?;

    console.print_footer()?;

    if failed {
        return Err(ExitCode(1).into());
    }
}