Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ surge-core = { path = "crates/surge-core", version = "1.0.0" }
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9"
serde_json = "1.0"
semver = "1.0.28"
sha2 = "0.11"
hex = "0.4"
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "stream", "json"] }
Expand Down
2 changes: 2 additions & 0 deletions crates/surge-cli/src/commands/demote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use surge_core::config::constants::{DEFAULT_ZSTD_LEVEL, RELEASES_FILE_COMPRESSED
use surge_core::config::manifest::SurgeManifest;
use surge_core::error::{Result, SurgeError};
use surge_core::releases::manifest::{compress_release_index, decompress_release_index};
use surge_core::releases::version::canonicalize_version;
use surge_core::storage::{self, StorageBackend};

/// Demote (remove) a release version from a channel.
Expand All @@ -19,6 +20,7 @@ pub async fn execute(
channel: &str,
) -> Result<()> {
const TOTAL_STAGES: usize = 4;
let version = canonicalize_version(version, "release version")?;

let theme = UiTheme::global();
let started = Instant::now();
Expand Down
8 changes: 5 additions & 3 deletions crates/surge-cli/src/commands/pack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use surge_core::releases::artifact_cache::{CacheFetchOutcome, fetch_or_reuse_fil
use surge_core::releases::restore::{
RestoreOptions, plan_full_archive_restore, restore_full_archive_for_version_with_options,
};
use surge_core::releases::version::canonicalize_version;
use surge_core::storage_config::build_storage_config;

/// Build release packages (full + delta) for a given app version and RID.
Expand All @@ -47,6 +48,7 @@ pub async fn execute(
output_dir: &Path,
) -> Result<()> {
const TOTAL_STAGES: usize = 5;
let version = canonicalize_version(version, "release version")?;

let theme = UiTheme::global();
let started = Instant::now();
Expand All @@ -62,7 +64,7 @@ pub async fn execute(

print_stage(theme, 2, TOTAL_STAGES, "Validating artifacts and output directories");
let artifacts_dir = artifacts_dir.map_or_else(
|| default_artifacts_dir(manifest_path, &app_id, &rid, version),
|| default_artifacts_dir(manifest_path, &app_id, &rid, &version),
PathBuf::from,
);
if !artifacts_dir.is_dir() {
Expand Down Expand Up @@ -96,7 +98,7 @@ pub async fn execute(
))
})?;

let mut builder = PackBuilder::new(ctx, manifest_path_s, &app_id, &rid, version, artifacts_dir_s)?;
let mut builder = PackBuilder::new(ctx, manifest_path_s, &app_id, &rid, &version, artifacts_dir_s)?;
let build_started = Instant::now();
let build_running = Arc::new(AtomicBool::new(true));
let build_step = Arc::new(AtomicI32::new(0));
Expand Down Expand Up @@ -168,7 +170,7 @@ pub async fn execute(
&target,
&app_id,
&rid,
version,
&version,
&self::resolution::default_channel_for_app(&manifest, app),
manifest_path.parent().unwrap_or_else(|| Path::new(".")),
artifacts_dir.as_path(),
Expand Down
1 change: 1 addition & 0 deletions crates/surge-cli/src/commands/pack/installers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ pub(super) fn build_installers_with_launcher(
environment: target.environment.clone(),
},
};
manifest_payload.validate()?;
let manifest_yaml = serde_yaml::to_string(&manifest_payload)?;
std::fs::write(staging.join("installer.yml"), manifest_yaml.as_bytes())?;

Expand Down
4 changes: 3 additions & 1 deletion crates/surge-cli/src/commands/promote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use surge_core::config::constants::{DEFAULT_ZSTD_LEVEL, RELEASES_FILE_COMPRESSED
use surge_core::config::manifest::SurgeManifest;
use surge_core::error::{Result, SurgeError};
use surge_core::releases::manifest::compress_release_index;
use surge_core::releases::version::canonicalize_version;
use surge_core::storage;

/// Promote a release version to a target channel.
Expand All @@ -19,6 +20,7 @@ pub async fn execute(
channel: &str,
) -> Result<()> {
const TOTAL_STAGES: usize = 5;
let version = canonicalize_version(version, "release version")?;

let theme = UiTheme::global();
let started = Instant::now();
Expand Down Expand Up @@ -71,7 +73,7 @@ pub async fn execute(
}

print_stage(theme, 3, TOTAL_STAGES, "Ensuring release full artifact exists");
let full_materialized = super::ensure_release_full_artifact(&*backend, &index, &rid, version).await?;
let full_materialized = super::ensure_release_full_artifact(&*backend, &index, &rid, &version).await?;
print_stage_done(
theme,
3,
Expand Down
80 changes: 49 additions & 31 deletions crates/surge-cli/src/commands/push.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ use surge_core::releases::manifest::{
DeltaArtifact, PATCH_FORMAT_BSDIFF4, PATCH_FORMAT_CHUNKED_BSDIFF_V1, PATCH_FORMAT_SPARSE_FILE_OPS_V1, ReleaseEntry,
ReleaseIndex, compress_release_index, decompress_release_index,
};
use surge_core::releases::restore::find_previous_release_for_rid;
use surge_core::releases::restore::required_artifacts_for_index;
use surge_core::releases::version::canonicalize_version;
use surge_core::releases::version::compare_versions;
use surge_core::storage::{self, StorageBackend};

Expand All @@ -41,6 +43,7 @@ pub async fn execute(
packages_dir: &Path,
) -> Result<()> {
const TOTAL_STAGES: usize = 5;
let version = canonicalize_version(version, "release version")?;

let theme = UiTheme::global();
let started = Instant::now();
Expand Down Expand Up @@ -159,7 +162,7 @@ pub async fn execute(
let pruned = update_release_index(
&*backend,
&app_id,
version,
&version,
&rid,
channel,
full_filename,
Expand Down Expand Up @@ -290,6 +293,19 @@ async fn update_release_index(
.releases
.iter()
.any(|release| release.rid == rid || release.rid.is_empty());
let delta_from_version = if delta_filename.trim().is_empty() {
None
} else {
Some(
find_previous_release_for_rid(&index, rid, version)
.map(|release| release.version.clone())
.ok_or_else(|| {
SurgeError::Config(format!(
"Cannot publish delta artifact for {app_id}/{rid} v{version} without a previous release baseline"
))
})?,
)
};

index
.releases
Expand Down Expand Up @@ -318,41 +334,43 @@ async fn update_release_index(
installers,
environment,
};
let primary_delta = if delta_filename.trim().is_empty() {
None
} else if delta_patch_format.eq_ignore_ascii_case(PATCH_FORMAT_SPARSE_FILE_OPS_V1) {
Some(DeltaArtifact::sparse_file_ops_zstd(
"primary",
"",
&delta_filename,
delta_size,
&delta_sha256,
))
} else if delta_patch_format.eq_ignore_ascii_case(PATCH_FORMAT_CHUNKED_BSDIFF_V1) {
Some(DeltaArtifact::chunked_bsdiff_zstd(
"primary",
"",
&delta_filename,
delta_size,
&delta_sha256,
))
} else if delta_patch_format.eq_ignore_ascii_case(PATCH_FORMAT_BSDIFF4) {
Some(DeltaArtifact::bsdiff_zstd(
"primary",
"",
&delta_filename,
delta_size,
&delta_sha256,
))
} else {
Some(DeltaArtifact::with_patch_format(
let primary_delta = match delta_from_version.as_deref() {
None => None,
Some(delta_from_version) if delta_patch_format.eq_ignore_ascii_case(PATCH_FORMAT_SPARSE_FILE_OPS_V1) => {
Some(DeltaArtifact::sparse_file_ops_zstd(
"primary",
delta_from_version,
&delta_filename,
delta_size,
&delta_sha256,
))
}
Some(delta_from_version) if delta_patch_format.eq_ignore_ascii_case(PATCH_FORMAT_CHUNKED_BSDIFF_V1) => {
Some(DeltaArtifact::chunked_bsdiff_zstd(
"primary",
delta_from_version,
&delta_filename,
delta_size,
&delta_sha256,
))
}
Some(delta_from_version) if delta_patch_format.eq_ignore_ascii_case(PATCH_FORMAT_BSDIFF4) => {
Some(DeltaArtifact::bsdiff_zstd(
"primary",
delta_from_version,
&delta_filename,
delta_size,
&delta_sha256,
))
}
Some(delta_from_version) => Some(DeltaArtifact::with_patch_format(
"primary",
"",
delta_from_version,
&delta_patch_format,
&delta_filename,
delta_size,
&delta_sha256,
))
)),
};
entry.set_primary_delta(primary_delta);
index.releases.push(entry);
Expand Down
1 change: 1 addition & 0 deletions crates/surge-cli/src/commands/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ pub async fn execute(dir: &Path, no_start: bool, stage: bool) -> Result<()> {

let manifest_bytes = std::fs::read(&manifest_path)?;
let manifest: InstallerManifest = serde_yaml::from_slice(&manifest_bytes)?;
manifest.validate()?;

logline::info(&format!(
"Setting up {} v{} ({}/{})",
Expand Down
1 change: 1 addition & 0 deletions crates/surge-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ quick-xml = { workspace = true }
async-trait = { workspace = true }
futures-util = { workspace = true }
tempfile = { workspace = true }
semver = { workspace = true }

[target.'cfg(unix)'.dependencies]
nix = { version = "0.31", features = ["signal", "process", "fs"] }
Expand Down
8 changes: 8 additions & 0 deletions crates/surge-core/src/config/installer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};

use crate::config::manifest::ShortcutLocation;
use crate::error::Result;
use crate::releases::version::validate_version_string;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
Expand Down Expand Up @@ -76,3 +78,9 @@ pub struct InstallerRuntime {
#[serde(default)]
pub environment: BTreeMap<String, String>,
}

impl InstallerManifest {
pub fn validate(&self) -> Result<()> {
validate_version_string(&self.version, "installer manifest version")
}
}
4 changes: 2 additions & 2 deletions crates/surge-core/src/install/activation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::error::{Result, SurgeError};
use crate::platform::fs::list_directories;
use crate::platform::paths::default_install_root;
use crate::platform::shortcuts::install_shortcuts;
use crate::releases::version::compare_versions;
use crate::releases::version::{compare_versions, is_valid_version_string};
use crate::supervisor::stub::find_latest_app_dir;

use super::persistent_assets::copy_persistent_assets;
Expand Down Expand Up @@ -211,7 +211,7 @@ pub fn install_package_locally_at_root_with_progress(

fn app_snapshot_version(dir_name: &str) -> Option<&str> {
let version = dir_name.strip_prefix("app-")?;
if version.is_empty() || !version.chars().next().is_some_and(|c| c.is_ascii_digit()) {
if !is_valid_version_string(version) {
return None;
}
Some(version)
Expand Down
5 changes: 4 additions & 1 deletion crates/surge-core/src/install/runtime_manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use serde::Serialize;

use crate::context::StorageProvider;
use crate::error::{Result, SurgeError};
use crate::releases::version::canonicalize_version;

use super::InstallProfile;

Expand Down Expand Up @@ -125,9 +126,11 @@ pub fn write_runtime_manifest(
profile: &InstallProfile<'_>,
metadata: &RuntimeManifestMetadata<'_>,
) -> Result<PathBuf> {
let version = canonicalize_version(metadata.version, "runtime manifest version")?;

let manifest = RuntimeManifestFile {
id: profile.app_id.trim(),
version: metadata.version.trim(),
version: &version,
channel: metadata.channel.trim(),
install_directory: profile.install_directory.trim(),
supervisor_id: profile.supervisor_id.trim(),
Expand Down
4 changes: 3 additions & 1 deletion crates/surge-core/src/pack/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::config::manifest::{PackPolicy, ShortcutLocation, SurgeManifest};
use crate::context::Context;
use crate::error::{Result, SurgeError};
use crate::platform::fs::write_file_atomic;
use crate::releases::version::canonicalize_version;
use crate::storage::{StorageBackend, create_storage_backend};

pub(crate) use self::staging::build_canonical_archive_from_directory;
Expand Down Expand Up @@ -114,6 +115,7 @@ impl PackBuilder {
version: &str,
artifacts_dir: &str,
) -> Result<Self> {
let version = canonicalize_version(version, "release version")?;
let manifest = SurgeManifest::from_file(Path::new(manifest_path))?;
let pack_policy = manifest.effective_pack_policy();
let (app, target) = manifest
Expand Down Expand Up @@ -157,7 +159,7 @@ impl PackBuilder {
ctx,
app_id: app_id.to_string(),
rid: rid.to_string(),
version: version.to_string(),
version,
name: app.effective_name(),
main_exe,
install_directory: app.effective_install_directory(),
Expand Down
3 changes: 3 additions & 0 deletions crates/surge-core/src/releases/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::config::constants::RELEASES_FILE_COMPRESSED;
use crate::context::Context;
use crate::error::{Result, SurgeError};
use crate::releases::manifest::{ReleaseEntry, ReleaseIndex, compress_release_index, decompress_release_index};
use crate::releases::version::canonicalize_version;

/// Manages release channels: fetching, saving, promoting, and demoting releases.
pub struct ChannelManager {
Expand Down Expand Up @@ -46,6 +47,7 @@ impl ChannelManager {
/// not on the source channel.
pub async fn promote(&self, version: &str, source_channel: &str, target_channel: &str) -> Result<()> {
self.ctx.check_cancelled()?;
let version = canonicalize_version(version, "release version")?;

let mut index = self.fetch_index().await?;
let mut found = false;
Expand Down Expand Up @@ -82,6 +84,7 @@ impl ChannelManager {
/// if the version is not found or not on the channel.
pub async fn demote(&self, version: &str, channel: &str) -> Result<()> {
self.ctx.check_cancelled()?;
let version = canonicalize_version(version, "release version")?;

let mut index = self.fetch_index().await?;
let mut found = false;
Expand Down
Loading