Skip to content
Merged
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
148 changes: 137 additions & 11 deletions crates/fbuild-build-esp/src/esp32/orchestrator/packages_platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,65 @@ pub(super) async fn pioarduino_platform(
),
));
}
warn_unhonored_platform_pin(env_config);
if let Some(source) = platform_repository_source(env_config)? {
// A `.git#<ref>` pin is resolved to an immutable commit and installed
// under that commit's archive URL, so two refs can never share a
// cached payload, and a ref is never replaced by stable
// (FastLED/fbuild#1662). The commit lock is reused by later builds.
if !fetch {
return Ok(None);
}
let cache_root = fbuild_packages::Cache::new(project_dir).platforms_dir();
let resolved = fbuild_packages::platformio_repository::resolve_cached_source(
&source,
&cache_root,
false,
)
.await
.map_err(|error| {
fbuild_core::FbuildError::PackageError(format!(
"cannot resolve ESP32 platform `{}`: {error}",
requested_platform(env_config)
))
})?;
tracing::info!(
"resolved requested ESP32 platform {} to {}",
requested_platform(env_config),
resolved.archive.url
);
return Ok(Some(
fbuild_packages::library::Esp32Platform::with_override(project_dir, resolved.archive),
));
}
reject_unhonored_platform_pin(env_config)?;
Ok(Some(fbuild_packages::library::Esp32Platform::new(
project_dir,
)))
}

fn requested_platform(env_config: Option<&HashMap<String, String>>) -> &str {
env_config
.and_then(|env| env.get("platform"))
.map_or("", |value| value.trim())
}

/// A `platform = <repository URL>[#ref]` pin. Archive URLs are handled by
/// `resolve_platform_override`; registry names by [`platform_registry_spec`].
fn platform_repository_source(
env_config: Option<&HashMap<String, String>>,
) -> Result<Option<PackageSource>> {
let platform = requested_platform(env_config);
if !platform.contains("://") && !platform.starts_with("git@") {
return Ok(None);
}
match parse_package_spec(platform) {
Ok(spec) if matches!(spec.source, PackageSource::Repository { .. }) => {
Ok(Some(spec.source))
}
_ => Ok(None),
}
}

/// The Arduino framework package. Override precedence (FastLED/fbuild#672):
/// 1. `platform_packages = framework-arduinoespressif32@<URL>#<sha>` wins
/// outright — consumer-supplied URL replaces the platform.json-derived
Expand Down Expand Up @@ -256,18 +309,20 @@ pub(super) async fn ensure_sdk_libs(
/// Name an unsupported non-archive URL instead of dropping it silently
/// (FastLED/fbuild#1407). Registry aliases are resolved above; other URLs
/// still fall back to the pioarduino stable platform.
fn warn_unhonored_platform_pin(env_config: Option<&HashMap<String, String>>) {
let Some(value) = env_config.and_then(|env| env.get("platform")) else {
return;
};
let value = value.trim();
/// A URL pin that is neither a release archive nor a repository cannot be
/// honored. Building pioarduino stable instead would report a green build of
/// an SDK the project did not ask for (FastLED/fbuild#1662).
fn reject_unhonored_platform_pin(env_config: Option<&HashMap<String, String>>) -> Result<()> {
let value = requested_platform(env_config);
if value.contains("://") {
tracing::warn!(
"platform pin `{value}` is not a downloadable archive URL; building with the \
pioarduino stable platform instead. Pin a release with `platform = \
https://github.com/pioarduino/platform-espressif32/releases/download/<tag>/platform-espressif32.zip`"
);
return Err(fbuild_core::FbuildError::PackageError(format!(
"ESP32 platform pin `{value}` is neither a release archive nor a repository URL. \
Pin a release with `platform = \
https://github.com/pioarduino/platform-espressif32/releases/download/<tag>/platform-espressif32.zip` \
or a repository with `platform = https://github.com/<owner>/<repo>.git#<ref>`"
)));
}
Ok(())
}

/// Provision the managed `tool-esptoolpy` package (the tasmota PyInstaller
Expand Down Expand Up @@ -382,6 +437,77 @@ pub(super) async fn esptool_package(
mod registry_framework_override_tests {
use super::*;

fn platform_env(platform: &str) -> HashMap<String, String> {
HashMap::from([("platform".into(), platform.into())])
}

#[test]
fn repository_ref_pin_is_a_repository_source() {
let env = platform_env("https://github.com/pioarduino/platform-espressif32.git#prep_IDF6");
let Some(PackageSource::Repository { url, reference }) =
platform_repository_source(Some(&env)).unwrap()
else {
panic!("expected a repository source");
};
assert_eq!(
url,
"https://github.com/pioarduino/platform-espressif32.git"
);
assert_eq!(reference.as_deref(), Some("prep_IDF6"));
}

#[test]
fn archive_and_registry_pins_are_not_repository_sources() {
for platform in [
"https://github.com/pioarduino/platform-espressif32/releases/download/55.03.311/platform-espressif32.zip",
"espressif32@6.9.0",
"espressif32",
] {
assert!(
platform_repository_source(Some(&platform_env(platform)))
.unwrap()
.is_none(),
"{platform}"
);
}
}

#[test]
fn unresolvable_url_pin_is_an_error_not_a_stable_fallback() {
let env = platform_env("https://example.test/not-a-platform");
assert!(reject_unhonored_platform_pin(Some(&env)).is_err());
assert!(reject_unhonored_platform_pin(Some(&platform_env("espressif32"))).is_ok());
assert!(reject_unhonored_platform_pin(None).is_ok());
}

#[tokio::test]
async fn cached_commit_lock_selects_that_commits_archive_without_network() {
let tmp = tempfile::TempDir::new().unwrap();
let env = platform_env("https://github.com/pioarduino/platform-espressif32.git#prep_IDF6");
let source = platform_repository_source(Some(&env)).unwrap().unwrap();
let commit = "0123456789abcdef0123456789abcdef01234567";
let request = serde_json::to_vec(&source).unwrap();
let key = format!("{:x}", Sha256::digest(request));
let cache_root = fbuild_packages::Cache::new(tmp.path()).platforms_dir();
let locks = cache_root.join("platformio-source-locks");
std::fs::create_dir_all(&locks).unwrap();
std::fs::write(
locks.join(format!("{key}.json")),
serde_json::to_vec(&fbuild_core::platformio_package::PackageLock::Repository {
url: "https://github.com/pioarduino/platform-espressif32.git".into(),
commit: commit.into(),
})
.unwrap(),
)
.unwrap();
let platform = pioarduino_platform(tmp.path(), Some(&env), true)
.await
.unwrap()
.unwrap();
let url = fbuild_packages::Package::get_info(&platform).url;
assert!(url.contains(commit), "{url}");
}

#[test]
fn framework_registry_pin_selects_registry_platform_for_unversioned_alias() {
let env = HashMap::from([
Expand Down
Loading