1use std::process::{Command, Stdio};
2
3use anyhow::{ensure, Context, Result};
4use chrono::Utc;
5use clap::Args;
6use semver::{Prerelease, Version};
7
8use crate::{
9 npm::util::{bump_swc_cli, set_version},
10 util::{repository_root, wrap},
11};
12
13#[derive(Debug, Args)]
14pub(super) struct NightlyCmd {}
15
16impl NightlyCmd {
17 pub fn run(self) -> Result<()> {
18 wrap(|| {
19 let date = Utc::now().format("%Y%m%d").to_string();
20
21 let root_pkg_json = repository_root()?.join("./packages/core/package.json");
22 let content = serde_json::from_reader::<_, serde_json::Value>(
23 std::fs::File::open(root_pkg_json)
24 .context("failed to open ./packages/core/package.json")?,
25 )?;
26 let prev_version = Version::parse(content["version"].as_str().unwrap())?;
27
28 let version = find_first_nightly(&prev_version, &date)?;
29
30 println!("Publishing nightly version {version}");
31
32 set_version(&version).context("failed to set version")?;
33 bump_swc_cli().context("failed to bump swc-cli")?;
34
35 let status = Command::new("sh")
37 .arg("./scripts/publish.sh")
38 .arg(format!("{version}"))
39 .status()
40 .context("failed to publish")?;
41
42 ensure!(status.success(), "failed to publish");
43
44 Ok(())
45 })
46 .context("failed to publish nightly version")
47 }
48}
49
50fn find_first_nightly(prev_version: &semver::Version, date: &str) -> Result<Version> {
51 let mut ver = prev_version.clone();
52
53 if prev_version.pre.is_empty() {
54 ver.patch += 1;
55 }
56
57 for i in 1.. {
58 ver.pre = Prerelease::new(&format!("nightly-{date}.{i}"))?;
59
60 if ver <= *prev_version {
61 continue;
62 }
63
64 let tag = format!("v{ver}");
65
66 let output = Command::new("git")
67 .arg("tag")
68 .arg("-l")
69 .stderr(Stdio::inherit())
70 .output()
71 .context("git tag -l failed")
72 .map(|v| v.stdout)
73 .and_then(|s| String::from_utf8(s).context("git tag -l returned non-utf8"))?;
74
75 if !output.contains(&tag) {
76 return Ok(ver);
77 }
78 }
79
80 unreachable!("failed to find a free nightly version")
81}