Skip to content
39 changes: 29 additions & 10 deletions ci/build-channel/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,6 @@ fn write_document(
) -> Result<()> {
for component in components {
println!("\nWriting package info for component '{}'", &component.name);
let tag_prefix = if component.name == "forc" {
"forc-binaries"
} else if component.name == "fuel-core" || component.name == "fuel-core-keygen" {
"fuel-core"
} else {
&component.name
};

let version = match component_versions.get(&component.name) {
Some(v) => v.clone(),
Expand All @@ -291,10 +284,11 @@ fn write_document(
// older versions downloadable from their original repositories.
let repo = component.repository_for_version(&version);
let tag = component.tag_for_version(&version);
let tarball_prefix = if tag_prefix == "forc-binaries" {
tag_prefix.to_string()
let tarball_prefix_base = component.tarball_prefix_for_version(&version);
let tarball_prefix = if tarball_prefix_base == "forc-binaries" {
tarball_prefix_base.to_string()
} else {
format!("{}-{}", tag_prefix, version)
format!("{}-{}", tarball_prefix_base, version)
};

document["pkg"][&component.name] = implicit_table();
Expand Down Expand Up @@ -403,4 +397,29 @@ mod tests {
assert!(!version.to_string().is_empty());
println!("fuel-core version: {}", version);
}

#[test]
fn test_forc_crypto_url_generation() {
let forc_crypto = Component::from_name("forc-crypto").unwrap();

// Legacy version (< 0.71.0) should use sway repo with forc-binaries tarball
let legacy = Version::new(0, 70, 1);
let repo = forc_crypto.repository_for_version(&legacy);
let tag = forc_crypto.tag_for_version(&legacy);
let prefix = forc_crypto.tarball_prefix_for_version(&legacy);
assert_eq!(
(repo, tag.as_str(), prefix),
("sway", "v0.70.1", "forc-binaries")
);

// Migrated version (>= 0.71.0) should use forc repo with forc-crypto tarball
let migrated = Version::new(0, 71, 0);
let repo = forc_crypto.repository_for_version(&migrated);
let tag = forc_crypto.tag_for_version(&migrated);
let prefix = forc_crypto.tarball_prefix_for_version(&migrated);
assert_eq!(
(repo, tag.as_str(), prefix),
("forc", "forc-crypto-0.71.0", "forc-crypto")
);
}
}
114 changes: 113 additions & 1 deletion component/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ pub struct Component {
/// Semver version cutoff (e.g., "0.16.0") before which `legacy_repository_name` is used.
/// Versions < this value use legacy repo, versions >= this value use current repo.
pub legacy_before: Option<String>,
/// Legacy tarball prefix for versions before the migration cutoff.
/// Used when the component was bundled differently (e.g., forc-crypto was in forc-binaries).
pub legacy_tarball_prefix: Option<String>,
}

impl Component {
Expand All @@ -59,6 +62,7 @@ impl Component {
show_fuels_version: Some(false),
legacy_repository_name: None,
legacy_before: None,
legacy_tarball_prefix: None,
});
}

Expand Down Expand Up @@ -89,14 +93,34 @@ impl Component {
&self.repository_name
}

/// Returns the tarball prefix to use for a given component version.
///
/// This allows components to change their packaging format over time while
/// keeping older versions downloadable with their original tarball prefix.
pub fn tarball_prefix_for_version<'a>(&'a self, version: &Version) -> &'a str {
if let (Some(legacy_prefix), Some(legacy_before)) =
(&self.legacy_tarball_prefix, &self.legacy_before)
{
if let Ok(cutoff) = Version::parse(legacy_before) {
if version < &cutoff {
return legacy_prefix;
}
}
}

&self.tarball_prefix
}

/// Returns the git tag format to use for a given component version.
///
/// Different repositories use different tag naming conventions. This method
/// returns the correct tag format based on the component and repository.
pub fn tag_for_version(&self, version: &Version) -> String {
let repo = self.repository_for_version(version);
match (self.name.as_str(), repo) {
("forc-wallet", "forc") => format!("forc-wallet-{}", version),
("forc-wallet", "forc") | ("forc-crypto", "forc") => {
format!("{}-{}", self.name, version)
}
_ => format!("v{}", version),
}
}
Expand Down Expand Up @@ -418,6 +442,69 @@ mod tests {
);
}

#[test]
fn test_repository_for_version_forc_crypto_migration() {
let components = Components::collect().unwrap();
let forc_crypto = components
.component
.get("forc-crypto")
.expect("forc-crypto component must exist");

let legacy = Version::new(0, 70, 1);
let migrated = Version::new(0, 71, 0);

assert_eq!(
forc_crypto.repository_for_version(&legacy),
"sway",
"pre-0.71.0 forc-crypto versions should use the sway repository"
);
assert_eq!(
forc_crypto.repository_for_version(&migrated),
"forc",
"0.71.0+ forc-crypto versions should use the forc monorepo"
);
}

#[test]
fn test_tarball_prefix_for_version_forc_crypto_migration() {
let components = Components::collect().unwrap();
let forc_crypto = components
.component
.get("forc-crypto")
.expect("forc-crypto component must exist");

let legacy = Version::new(0, 70, 1);
let migrated = Version::new(0, 71, 0);

assert_eq!(
forc_crypto.tarball_prefix_for_version(&legacy),
"forc-binaries",
"pre-0.71.0 forc-crypto was bundled in forc-binaries"
);
assert_eq!(
forc_crypto.tarball_prefix_for_version(&migrated),
"forc-crypto",
"0.71.0+ forc-crypto has its own tarball"
);
}

#[test]
fn test_tarball_prefix_for_version_no_legacy() {
let components = Components::collect().unwrap();
let forc = components
.component
.get("forc")
.expect("forc component must exist");

// Components without legacy_tarball_prefix should always use tarball_prefix
let version = Version::new(0, 50, 0);
assert_eq!(
forc.tarball_prefix_for_version(&version),
"forc-binaries",
"Components without legacy_tarball_prefix should use tarball_prefix"
);
}

#[test]
fn test_tag_for_version_forc_wallet_migration() {
let components = Components::collect().unwrap();
Expand Down Expand Up @@ -451,6 +538,31 @@ mod tests {
);
}

#[test]
fn test_tag_for_version_forc_crypto_migration() {
let components = Components::collect().unwrap();
let forc_crypto = components
.component
.get("forc-crypto")
.expect("forc-crypto component must exist");

// Legacy versions (< 0.71.0) should use standard v-prefixed tags
let legacy = Version::new(0, 70, 1);
assert_eq!(
forc_crypto.tag_for_version(&legacy),
"v0.70.1",
"Legacy forc-crypto versions should use v-prefixed tags"
);

// New versions (>= 0.71.0) in forc repo should use forc-crypto-prefixed tags
let migrated = Version::new(0, 71, 0);
assert_eq!(
forc_crypto.tag_for_version(&migrated),
"forc-crypto-0.71.0",
"Migrated forc-crypto versions should use forc-crypto-prefixed tags"
);
}

#[test]
fn test_tag_for_version_standard_components() {
let components = Components::collect().unwrap();
Expand Down
10 changes: 7 additions & 3 deletions components.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,15 @@ targets = ["linux_amd64", "linux_arm64", "darwin_amd64", "darwin_arm64"]

[component.forc-crypto]
name = "forc-crypto"
tarball_prefix = "forc-binaries"
tarball_prefix = "forc-crypto"
is_plugin = true
executables = ["forc-crypto"]
repository_name = "sway"
repository_name = "forc"
legacy_repository_name = "sway"
legacy_before = "0.71.0"
legacy_tarball_prefix = "forc-binaries"
targets = ["linux_amd64", "linux_arm64", "darwin_amd64", "darwin_arm64"]
publish = true

[component.forc-lsp]
name = "forc-lsp"
Expand Down Expand Up @@ -111,6 +115,6 @@ executables = ["forc-wallet"]
repository_name = "forc"
legacy_repository_name = "forc-wallet"
legacy_before = "0.16.0"
targets = [ "aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu", "aarch64-apple-darwin", "x86_64-apple-darwin" ]
targets = ["linux_amd64", "linux_arm64", "darwin_amd64", "darwin_arm64"]
publish = true
show_fuels_version = true
3 changes: 2 additions & 1 deletion src/download.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ impl DownloadCfg {
let tarball_url = github_releases_download_url(FUELUP, &version, &tarball_name);
(tarball_name, tarball_url)
} else if let Ok(component) = Component::from_name(name) {
let tarball_name = tarball_name(&component.tarball_prefix, &version, &target);
let tarball_prefix = component.tarball_prefix_for_version(&version);
let tarball_name = tarball_name(tarball_prefix, &version, &target);
let repo = component.repository_for_version(&version);
let tag = component.tag_for_version(&version);
let tarball_url = github_releases_download_url_with_tag(repo, &tag, &tarball_name);
Expand Down
29 changes: 9 additions & 20 deletions src/proxy_cli.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::{
download::DownloadCfg,
store::Store,
target_triple::TargetTriple,
toolchain::{DistToolchainDescription, Toolchain},
toolchain_override::{ComponentSpec, ToolchainOverride},
Expand Down Expand Up @@ -58,25 +57,15 @@ fn direct_proxy(proc_name: &str, args: &[OsString], toolchain: &Toolchain) -> Re
if let Some(spec) = to.get_component_spec(component_name) {
match spec {
ComponentSpec::Version(version) => {
// For version specs, get component from store
let store = Store::from_env()?;

if !store.has_component(component_name, version) {
let download_cfg = DownloadCfg::new(
component_name,
TargetTriple::from_component(component_name)?,
Some(version.clone()),
)?;
// Install components within [components] that are declared but missing from the store.
store.install_component(&download_cfg)?;
};

(
store
.component_dir_path(component_name, version)
.join(proc_name),
description.to_string(),
)
// For version specs, ensure the component is linked into the toolchain bin
let download_cfg = DownloadCfg::new(
component_name,
TargetTriple::from_component(component_name)?,
Some(version.clone()),
)?;
toolchain.add_component(download_cfg)?;

(toolchain.bin_path.join(proc_name), description.to_string())
}
ComponentSpec::Path(_) => {
// For path specs, validate this specific component and use the resolved path
Expand Down
Loading
Loading