Skip to main content

opal/
lib.rs

1use serde::Deserialize;
2use std::path::PathBuf;
3use std::str::FromStr;
4use structopt::StructOpt;
5
6pub mod ai;
7pub mod compiler;
8pub mod config;
9pub mod display;
10pub mod engine;
11pub mod env;
12pub mod execution_plan;
13pub mod executor;
14pub mod git;
15pub mod gitlab;
16pub mod history;
17pub mod logging;
18pub mod model;
19pub mod naming;
20pub mod pipeline;
21pub mod runner;
22pub mod runtime;
23pub mod secrets;
24pub mod terminal;
25pub mod ui;
26
27#[derive(StructOpt)]
28#[structopt(name = "opal", version = env!("CARGO_PKG_VERSION"))]
29pub struct Cli {
30    #[structopt(
31        short,
32        long,
33        default_value = "info",
34        possible_values = &["trace", "debug", "info", "warn", "error"]
35    )]
36    pub log_level: tracing::Level,
37
38    #[structopt(subcommand)]
39    pub commands: Commands,
40}
41
42#[derive(StructOpt)]
43pub enum Commands {
44    Run(RunArgs),
45    Plan(PlanArgs),
46    View(ViewArgs),
47}
48
49#[derive(StructOpt)]
50pub struct RunArgs {
51    /// Which .gitlab-ci.yml file to use (defaults to <workdir>/.gitlab-ci.yml)
52    #[structopt(short, long)]
53    pub pipeline: Option<PathBuf>,
54
55    #[structopt(short, long)]
56    /// Context directory (defaults to current working directory)
57    pub workdir: Option<PathBuf>,
58
59    #[structopt(short, long)]
60    /// Optional base image override; falls back to pipeline/job image.
61    pub base_image: Option<String>,
62
63    #[structopt(
64        short = "E",
65        long = "env",
66        value_name = "GLOB",
67        help = "Include host env vars matching this glob (e.g. APP_*). Repeat to add more."
68    )]
69    pub env_includes: Vec<String>,
70
71    #[structopt(long = "max-parallel-jobs", default_value = "5")]
72    /// Maximum number of jobs to run concurrently
73    pub max_parallel_jobs: usize,
74
75    #[structopt(long = "trace-scripts")]
76    /// Print each job command as it runs (enables shell `set -x`)
77    pub trace_scripts: bool,
78
79    #[structopt(
80        long = "engine",
81        default_value = "auto",
82        possible_values = EngineChoice::VARIANTS,
83        help = "Container runtime to use (auto, container, docker, podman, nerdctl, orbstack). nerdctl is Linux-specific in Opal."
84    )]
85    pub engine: EngineChoice,
86
87    #[structopt(long = "no-tui")]
88    /// Disable the Ratatui interface
89    pub no_tui: bool,
90
91    #[structopt(long = "gitlab-base-url", env = "OPAL_GITLAB_BASE_URL")]
92    /// Base URL for GitLab API (default: https://gitlab.com)
93    pub gitlab_base_url: Option<String>,
94
95    #[structopt(long = "gitlab-token", env = "OPAL_GITLAB_TOKEN")]
96    /// Personal access token used when downloading cross-project artifacts
97    pub gitlab_token: Option<String>,
98
99    #[structopt(long = "job", value_name = "NAME")]
100    /// Limit execution to selected jobs plus their required upstream dependencies. Repeat to select multiple jobs.
101    pub jobs: Vec<String>,
102}
103
104#[derive(StructOpt)]
105pub struct ViewArgs {
106    #[structopt(short, long)]
107    /// Context directory (defaults to current working directory)
108    pub workdir: Option<PathBuf>,
109}
110
111#[derive(StructOpt)]
112pub struct PlanArgs {
113    /// Which .gitlab-ci.yml file to inspect (defaults to <workdir>/.gitlab-ci.yml)
114    #[structopt(short, long)]
115    pub pipeline: Option<PathBuf>,
116
117    #[structopt(short, long)]
118    /// Context directory (defaults to current working directory)
119    pub workdir: Option<PathBuf>,
120
121    #[structopt(long = "gitlab-base-url", env = "OPAL_GITLAB_BASE_URL")]
122    /// Base URL for GitLab API (default: https://gitlab.com)
123    pub gitlab_base_url: Option<String>,
124
125    #[structopt(long = "gitlab-token", env = "OPAL_GITLAB_TOKEN")]
126    /// Personal access token used when resolving cross-project includes
127    pub gitlab_token: Option<String>,
128
129    #[structopt(long = "job", value_name = "NAME")]
130    /// Limit planning to selected jobs plus their required upstream dependencies. Repeat to select multiple jobs.
131    pub jobs: Vec<String>,
132
133    #[structopt(long = "no-pager")]
134    /// Print the plan directly instead of opening it in a pager
135    pub no_pager: bool,
136
137    #[structopt(long = "json")]
138    /// Emit the execution plan as JSON
139    pub json: bool,
140}
141
142#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize)]
143#[serde(rename_all = "lowercase")]
144pub enum EngineChoice {
145    Auto,
146    Container,
147    Docker,
148    Podman,
149    Nerdctl,
150    Orbstack,
151}
152
153impl EngineChoice {
154    pub const VARIANTS: &'static [&'static str] = &[
155        "auto",
156        "container",
157        "docker",
158        "podman",
159        "nerdctl",
160        "orbstack",
161    ];
162}
163
164impl FromStr for EngineChoice {
165    type Err = String;
166
167    fn from_str(s: &str) -> Result<Self, Self::Err> {
168        match s.to_ascii_lowercase().as_str() {
169            "auto" => Ok(Self::Auto),
170            "container" => Ok(Self::Container),
171            "docker" => Ok(Self::Docker),
172            "podman" => Ok(Self::Podman),
173            "nerdctl" => Ok(Self::Nerdctl),
174            "orbstack" => Ok(Self::Orbstack),
175            other => Err(format!("unknown engine '{other}'")),
176        }
177    }
178}
179
180#[derive(Clone, Copy, Debug)]
181pub enum EngineKind {
182    ContainerCli,
183    Docker,
184    Podman,
185    Nerdctl,
186    Orbstack,
187}
188
189#[derive(Debug, Clone)]
190pub struct ExecutorConfig {
191    pub image: Option<String>,
192    pub workdir: PathBuf,
193    pub pipeline: PathBuf,
194    pub env_includes: Vec<String>,
195    pub selected_jobs: Vec<String>,
196    pub max_parallel_jobs: usize,
197    pub enable_tui: bool,
198    pub engine: EngineKind,
199    pub gitlab: Option<GitLabRemoteConfig>,
200    pub settings: config::OpalConfig,
201    pub trace_scripts: bool,
202}
203
204#[derive(Clone, Debug)]
205pub struct GitLabRemoteConfig {
206    pub base_url: String,
207    pub token: String,
208}