Skip to content
Merged
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
19 changes: 6 additions & 13 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,23 @@ on:

jobs:
cli:
runs-on: ubuntu-latest
runs-on: self-hosted
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@main
- uses: DeterminateSystems/nix-installer-action@b92f66560d6f97d6576405a7bae901ab57e72b6a # pin@main
# with:
# nix-installer-tag: v0.16.1 # https://github.com/DeterminateSystems/nix-installer/tags
- uses: DeterminateSystems/magic-nix-cache-action@a76a83091cd8728db8c37312dbdd0eeb1177a6c0 # pin@main
- uses: DeterminateSystems/flake-checker-action@078f5f7f47ee188aa6cb472527ca5984e195222d # pin@main
- uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2 # pin@main
- name: Check flake
run: nix flake check

examples:
runs-on: ubuntu-latest
runs-on: self-hosted
strategy:
fail-fast: false
matrix:
example:
- external-deps
- marlin
- multi-env
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@main
- uses: DeterminateSystems/nix-installer-action@b92f66560d6f97d6576405a7bae901ab57e72b6a # pin@main
# with:
# nix-installer-tag: v0.16.1 # https://github.com/DeterminateSystems/nix-installer/tags
- uses: DeterminateSystems/magic-nix-cache-action@a76a83091cd8728db8c37312dbdd0eeb1177a6c0 # pin@main
- uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2 # pin@main
- name: Check flake
working-directory: examples/${{ matrix.example }}
run: nix flake check
107 changes: 46 additions & 61 deletions cli/src/lockfile.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
use std::{collections::BTreeMap, fmt::Display, path::PathBuf};
use std::{collections::BTreeMap, fmt::Display};

use base64::prelude::*;
use serde::{Deserialize, Serialize};
use url::Url;

use crate::{
manifest::{ExternalSpec, PackageManifest},
registry,
registry::{self, SystemSpec},
};

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(tag = "version")]
pub enum Lockfile {
V1 {
#[serde(rename = "2")]
V2 {
dependencies: BTreeMap<String, Dependency>,
},
}

impl Default for Lockfile {
fn default() -> Self {
Self::V1 {
Self::V2 {
dependencies: BTreeMap::default(),
}
}
}

impl Lockfile {
pub fn add_dependency(&mut self, dependency: Dependency) {
let Self::V1 { dependencies, .. } = self;
if let Some(old) = dependencies.insert(dependency.name.clone(), dependency) {
let new = &dependencies[&old.name];
pub fn add_dependency(&mut self, install_path: String, dependency: Dependency) {
let Self::V2 { dependencies, .. } = self;
if let Some(old) = dependencies.insert(install_path.clone(), dependency) {
let new = &dependencies[&install_path];
if old.manifest != new.manifest {
log::warn!(r#"Found duplicate dependency "{old}", using "{new}"#)
}
}
};
}
}

Expand Down Expand Up @@ -69,74 +70,58 @@ impl NixSystem {
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Dependency {
pub name: String,
pub install_path: PathBuf,
pub version: String,
pub manifest: String,
pub systems: BTreeMap<NixSystem, FetchUrl>,
pub manifest: PackageManifest,
pub src: Src,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub enum Src {
Universal(FetchUrl),
Systems(BTreeMap<NixSystem, FetchUrl>),
}

impl Dependency {
pub fn from_url(
manifest: &PackageManifest,
install_path: PathBuf,
package_spec: &ExternalSpec,
sha256: &[u8],
) -> Self {
let systems = NixSystem::ALL
.iter()
.map(|nix_system| (*nix_system, FetchUrl::new(package_spec.uri.clone(), sha256)))
.collect();
Self::new(
manifest,
install_path,
package_spec.name.clone(),
manifest.version.clone(),
systems,
)
pub fn from_url(manifest: PackageManifest, package_spec: &ExternalSpec, sha256: &[u8]) -> Self {
let src = Src::Universal(FetchUrl::new(package_spec.uri.clone(), sha256));
Self::new(manifest, package_spec.name.clone(), src)
}

pub fn from_registry(
manifest: &PackageManifest,
install_path: PathBuf,
package_spec: registry::PackageSpec,
) -> Self {
let version = package_spec.version;
let systems = NixSystem::ALL
pub fn from_registry(manifest: PackageManifest, package_spec: registry::PackageSpec) -> Self {
let src = if let Some(universal) = package_spec
.version
.files
.iter()
.filter_map(|nix_system| {
let file = version.supports(&nix_system.to_registry());
file.map(|file| (*nix_system, FetchUrl::from(file)))
})
.collect();
Self::new(
manifest,
install_path,
package_spec.name,
version.name.clone(),
systems,
)
.find(|f| f.system == SystemSpec::Wildcard)
{
Src::Universal(FetchUrl::from(universal))
} else {
Src::Systems(
NixSystem::ALL
.iter()
.filter_map(|nix_system| {
let file = package_spec.version.supports(&nix_system.to_registry());
file.map(|file| (*nix_system, FetchUrl::from(file)))
})
.collect(),
)
};

Self::new(manifest, package_spec.name, src)
}

fn new(
manifest: &PackageManifest,
install_path: PathBuf,
name: String,
version: String,
systems: BTreeMap<NixSystem, FetchUrl>,
) -> Self {
fn new(manifest: PackageManifest, name: String, src: Src) -> Self {
Self {
name,
install_path,
manifest: serde_json::to_string(manifest).expect("serializable manifest"),
version,
systems,
manifest,
src,
}
}
}

impl Display for Dependency {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}@{}", self.name, self.version)
write!(f, "{}@{}", self.name, self.manifest.version)
}
}

Expand Down
12 changes: 8 additions & 4 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ impl Args {
return Ok(core_dir.to_owned());
}

if let Some(core_dir) = env::var("PLATFORMIO_CORE_DIR").ok() {
if let Ok(core_dir) = env::var("PLATFORMIO_CORE_DIR") {
return Ok(PathBuf::from(core_dir));
}

#[expect(deprecated)] // nix doesn't support Windows anyway
if let Some(home_dir) = env::home_dir() {
return Ok(home_dir.join(".platformio"));
}
Expand All @@ -55,7 +56,7 @@ impl Args {
return Ok(Some(workspace_dir.to_owned()));
}

if let Some(workspace_dir) = env::var("PLATFORMIO_WORKSPACE_DIR").ok() {
if let Ok(workspace_dir) = env::var("PLATFORMIO_WORKSPACE_DIR") {
return Ok(Some(PathBuf::from(workspace_dir)));
}

Expand Down Expand Up @@ -99,8 +100,11 @@ async fn main() -> eyre::Result<()> {
let mut lockfile = Lockfile::default();

for artifact in global.into_iter().chain(workspace.into_iter()) {
let dependency = client.resolve(artifact).await?;
lockfile.add_dependency(dependency);
let dependency = client.resolve(artifact.manifest).await?;
lockfile.add_dependency(
artifact.install_path.to_string_lossy().into_owned(),
dependency,
);
}

println!("{}", serde_json::to_string_pretty(&lockfile)?);
Expand Down
30 changes: 14 additions & 16 deletions cli/src/manifest.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{
collections::BTreeMap,
fs,
path::{Path, PathBuf},
};

Expand All @@ -15,7 +16,7 @@ pub struct Artifact {
}

/// .piopm package manifest file
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Eq, PartialEq, Clone, Debug)]
pub struct PackageManifest {
#[serde(rename = "type")]
pub ty: PackageType,
Expand Down Expand Up @@ -45,22 +46,22 @@ impl PackageType {
}
}

#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Eq, PartialEq, Clone, Debug)]
#[serde(untagged)]
pub enum PackageSpec {
PlatformIO(PlatformIOSpec),
External(ExternalSpec),
}

#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Eq, PartialEq, Clone, Debug)]
pub struct PlatformIOSpec {
pub owner: String,
pub name: String,
#[serde(flatten)]
_extra: BTreeMap<String, Value>,
}

#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Eq, PartialEq, Clone, Debug)]
pub struct ExternalSpec {
pub name: String,
pub uri: Url,
Expand All @@ -70,25 +71,28 @@ pub struct ExternalSpec {

pub fn extract_artifacts(root: &Path) -> eyre::Result<Vec<Artifact>> {
let mut artifacts = vec![];
extract_artifacts_rec(&mut artifacts, &root, &root)?;
extract_artifacts_rec(&mut artifacts, &PathBuf::default(), root)?;
Ok(artifacts)
}

fn extract_artifacts_rec(
artifacts: &mut Vec<Artifact>,
root: &Path,
parent: &Path,
dir: &Path,
) -> eyre::Result<()> {
for entry in std::fs::read_dir(dir).with_context(|| format!("reading {dir:?}"))? {
let entry = entry?;
if !entry.file_type()?.is_dir() {

let path = fs::canonicalize(entry.path())?;
if !path.is_dir() {
continue;
}
let path = entry.path();

let parent = parent.join(entry.file_name());

let piopm = path.join(".piopm");
if !piopm.exists() {
extract_artifacts_rec(artifacts, &root, &path)?;
extract_artifacts_rec(artifacts, &parent, &path)?;
continue;
}

Expand All @@ -98,15 +102,9 @@ fn extract_artifacts_rec(
serde_path_to_error::deserialize::<_, PackageManifest>(de).wrap_err_with(|| {
format!("failed to parse manifest file: {}", piopm.to_string_lossy())
})?;
let install_path = path
.strip_prefix(root)
.wrap_err_with(|| {
format!("File {dir:?} is not a child of {root:?}: followed a symlink?")
})?
.to_path_buf();
artifacts.push(Artifact {
manifest,
install_path,
install_path: parent,
});
}

Expand Down
Loading