posthog_cli/sourcemaps/hermes/
clone.rs

1use std::path::PathBuf;
2
3use anyhow::{anyhow, Result};
4use tracing::info;
5
6use crate::{
7    invocation_context::context,
8    sourcemaps::{content::SourceMapFile, inject::get_release_for_maps},
9};
10
11#[derive(clap::Args)]
12pub struct CloneArgs {
13    /// The path of the minified source map
14    #[arg(short, long)]
15    pub minified_map_path: PathBuf,
16
17    /// The path of the composed source map
18    #[arg(short, long)]
19    pub composed_map_path: PathBuf,
20
21    /// The project name associated with the uploaded chunks. Required to have the uploaded chunks associated with
22    /// a specific release. We will try to auto-derive this from git information if not provided. Strongly recommended
23    /// to be set explicitly during release CD workflows. Only necessary if no project was provided during injection.
24    #[arg(long)]
25    pub project: Option<String>,
26
27    /// The version of the project - this can be a version number, semantic version, or a git commit hash. Required
28    /// to have the uploaded chunks associated with a specific release.
29    #[arg(long)]
30    pub version: Option<String>,
31}
32
33pub fn clone(args: &CloneArgs) -> Result<()> {
34    context().capture_command_invoked("hermes_clone");
35
36    let CloneArgs {
37        minified_map_path,
38        composed_map_path,
39        project,
40        version,
41    } = args;
42
43    let mut minified_map = SourceMapFile::load(minified_map_path).map_err(|e| {
44        anyhow!(
45            "Failed to load minfied map at '{}': {}",
46            minified_map_path.display(),
47            e
48        )
49    })?;
50
51    let mut composed_map = SourceMapFile::load(composed_map_path).map_err(|e| {
52        anyhow!(
53            "Failed to load composed map at '{}': {}",
54            minified_map_path.display(),
55            e
56        )
57    })?;
58
59    let release_id = get_release_for_maps(minified_map_path, project, version, [&minified_map])?
60        .map(|r| r.id.to_string());
61
62    // The flow here differs from plain sourcemap injection a bit - here, we don't ever
63    // overwrite the chunk ID, because at this point in the build process, we no longer
64    // control what chunk ID is inside the compiled hermes byte code bundle. So, instead,
65    // we permit e.g. uploading the same chunk ID's to two different posthog envs with two
66    // different release ID's, or arbitrarily re-running the upload command, but if someone
67    // tries to run `clone` twice, changing release but not posthog env, we'll error out. The
68    // correct way to upload the same set of artefacts to the same posthog env as part of
69    // two different releases is, 1, not to, but failing that, 2, to re-run the bundling process
70    if !minified_map.has_release_id() || minified_map.get_release_id() != release_id {
71        minified_map.set_release_id(release_id.clone());
72        minified_map.save()?;
73    }
74
75    // Copy metadata from source map to composed map
76    if let Some(chunk_id) = minified_map.get_chunk_id() {
77        composed_map.set_chunk_id(Some(chunk_id));
78    }
79
80    if let Some(release_id) = minified_map.get_release_id() {
81        composed_map.set_release_id(Some(release_id));
82    }
83
84    composed_map.save()?;
85    info!(
86        "Successfully cloned metadata to {}",
87        composed_map.inner.path.display()
88    );
89
90    info!("Finished cloning metadata");
91    Ok(())
92}