posthog_cli/
commands.rs

1use clap::{Parser, Subcommand};
2
3use crate::{
4    error::CapturedError,
5    experimental::{query::command::QueryCommand, tasks::TaskCommand},
6    invocation_context::{context, init_context},
7    sourcemaps::SourcemapCommand,
8};
9
10#[derive(Parser)]
11#[command(version, about, long_about = None)]
12pub struct Cli {
13    /// The PostHog host to connect to
14    #[arg(long)]
15    host: Option<String>,
16
17    #[command(subcommand)]
18    command: Commands,
19}
20
21#[derive(Subcommand)]
22pub enum Commands {
23    /// Interactively authenticate with PostHog, storing a personal API token locally. You can also use the
24    /// environment variables `POSTHOG_CLI_TOKEN` and `POSTHOG_CLI_ENV_ID`
25    Login,
26
27    /// Experimental commands, not quite ready for prime time
28    Exp {
29        #[command(subcommand)]
30        cmd: ExpCommand,
31    },
32
33    #[command(about = "Upload a directory of bundled chunks to PostHog")]
34    Sourcemap {
35        #[command(subcommand)]
36        cmd: SourcemapCommand,
37    },
38}
39
40#[derive(Subcommand)]
41pub enum ExpCommand {
42    /// Manage tasks - list, create, update, delete etc
43    Task {
44        #[command(subcommand)]
45        cmd: TaskCommand,
46        /// Whether to skip SSL verification when talking to the posthog API - only use when using self-signed certificates for
47        /// self-deployed instances
48        // TODO - it seems likely we won't support tasks for self hosted, but I'm putting this here in case we do
49        #[arg(long, default_value = "false")]
50        skip_ssl_verification: bool,
51    },
52
53    /// Run a SQL query against any data you have in posthog. This is mostly for fun, and subject to change
54    Query {
55        #[command(subcommand)]
56        cmd: QueryCommand,
57    },
58}
59
60impl Cli {
61    pub fn run() -> Result<(), CapturedError> {
62        let command = Cli::parse();
63
64        match command.command {
65            Commands::Login => {
66                // Notably login doesn't have a context set up going it - it sets one up
67                crate::login::login()?;
68            }
69            Commands::Sourcemap { cmd } => match cmd {
70                SourcemapCommand::Inject(input_args) => {
71                    init_context(command.host.clone(), false)?;
72                    crate::sourcemaps::inject::inject(&input_args)?;
73                }
74                SourcemapCommand::Upload(upload_args) => {
75                    init_context(command.host.clone(), upload_args.skip_ssl_verification)?;
76                    crate::sourcemaps::upload::upload_cmd(upload_args.clone())?;
77                }
78                SourcemapCommand::Process(args) => {
79                    init_context(command.host.clone(), args.skip_ssl_verification)?;
80                    let (inject, upload) = args.into();
81                    crate::sourcemaps::inject::inject(&inject)?;
82                    crate::sourcemaps::upload::upload_cmd(upload)?;
83                }
84            },
85            Commands::Exp { cmd } => match cmd {
86                ExpCommand::Task {
87                    cmd,
88                    skip_ssl_verification,
89                } => {
90                    init_context(command.host.clone(), skip_ssl_verification)?;
91                    cmd.run()?;
92                }
93                ExpCommand::Query { cmd } => {
94                    init_context(command.host.clone(), false)?;
95                    crate::experimental::query::command::query_command(&cmd)?
96                }
97            },
98        }
99
100        context().finish();
101
102        Ok(())
103    }
104}