Skip to content

Commit 077160a

Browse files
authored
Merge pull request #262 from helsing-ai/mara/fs-deps
Local Dependencies
2 parents 739d002 + 319cc6b commit 077160a

File tree

62 files changed

+648
-110
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+648
-110
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "buffrs"
3-
version = "0.8.1"
3+
version = "0.9.0"
44
edition = "2021"
55
description = "Modern protobuf package management"
66
authors = [

docs/src/guide/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Buffrs to develop and consume protocol buffer packages.
99
* [Dependencies](./dependencies.md)
1010
* [Creating a Package](./creating-a-package.md)
1111
* [Consuming Packages](./consuming-packages.md)
12+
* [Local Dependencies](./local-dependencies.md)
1213
* [Import System](./import-system.md)
1314
* [Project Layout](./project-layout.md)
1415
* [Manifest vs Lockfile](./manifest-vs-lockfile.md)

docs/src/guide/local-dependencies.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
## Local Dependencies
2+
3+
When working on larger projects or in monorepo projects setups you may find
4+
yourself in the situation to consume a locally defined buffrs project.
5+
6+
Imagine the following project setup:
7+
8+
```
9+
mono
10+
├── build.rs
11+
├── Cargo.toml
12+
├── Proto.toml
13+
├── Proto.toml
14+
├── proto
15+
| └── mono.proto
16+
└── src
17+
└── main.rs
18+
```
19+
20+
In this scenario the buffrs project for `mono-api` and the cargo project for
21+
the `mono-server` are setup in the very same directory, which is totally fine
22+
as long as this server does not require other buffrs api packages to be
23+
compiled!
24+
25+
#### Problem
26+
27+
Adding a dependency on other (unrelated api packages to `mono-api`) is
28+
complicated in the above scenario because its not clear to buffrs whether you
29+
are trying to reuse third party api definitions for your api or just wanting to
30+
install protos for compilation.
31+
32+
Hence buffrs will throw you an error if you try to publish an api package with
33+
dependencies on other apis!
34+
35+
```
36+
Error: × failed to publish `mono-api` to `http://<registry-uri>/<repository>`
37+
╰─▶ depending on API packages is not allowed
38+
```
39+
40+
#### Solution
41+
42+
Gladly buffrs offers a builtin solution for this! You can separate the
43+
`mono-api` buffrs package (used to publish your api) from the `mono-server`
44+
buffrs projects (used to install protos for compiling the server).
45+
46+
A monorepo setup here could look like this:
47+
48+
```
49+
mono
50+
├── mono-api
51+
| ├── Proto.toml
52+
| └── proto
53+
| └── mono.proto
54+
└── mono-server
55+
├── build.rs
56+
├── Cargo.toml
57+
├── Proto.toml
58+
├── proto
59+
| └── vendor
60+
└── src
61+
└── main.rs
62+
```
63+
64+
Where `mono/mono-api/Proto.toml` has this content:
65+
66+
```
67+
edition = "0.9"
68+
69+
[package]
70+
type = "api"
71+
name = "mono-api"
72+
version = "0.1.0"
73+
```
74+
75+
And `mono/mono-server/Proto.toml` has this content:
76+
77+
```
78+
edition = "0.9"
79+
80+
[dependencies]
81+
mono-api = { path = "../api" }
82+
third-party-api = { version = "=1.0.0", repository = "some-repo", registry = "http://..." }
83+
```
84+
85+
This enables you to:
86+
87+
- Independently publish `mono-api` using `buffrs publish` / `buffrs package`
88+
- Independently declare dependencies for `mono-server`
89+
90+
#### Caveats
91+
92+
Please note that projects containing any local dependencies can not be
93+
published anymore. The ability to declare local filesystem dependencies is
94+
mainly useful for the above scenario where you want to install buffrs packages
95+
for your server from different locations on the filesystem (monorepo, git
96+
submodules etc).

src/command.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::{
1919
manifest::{Dependency, Manifest, PackageManifest, MANIFEST_FILE},
2020
package::{PackageName, PackageStore, PackageType},
2121
registry::{Artifactory, RegistryUri},
22-
resolver::DependencyGraph,
22+
resolver::{DependencyGraph, ResolvedDependency},
2323
};
2424

2525
use async_recursion::async_recursion;
@@ -390,26 +390,31 @@ pub async fn install() -> miette::Result<()> {
390390
"unexpected error: missing dependency in dependency graph"
391391
))?;
392392

393-
store.unpack(&resolved.package).await.wrap_err(miette!(
393+
store.unpack(resolved.package()).await.wrap_err(miette!(
394394
"failed to unpack package {}",
395-
&resolved.package.name()
395+
&resolved.package().name()
396396
))?;
397397

398398
tracing::info!(
399399
"{} installed {}@{}",
400400
if prefix.is_empty() { "::" } else { &prefix },
401401
name,
402-
resolved.package.version()
402+
resolved.package().version()
403403
);
404404

405-
locked.push(resolved.package.lock(
406-
resolved.registry.clone(),
407-
resolved.repository.clone(),
408-
resolved.dependants.len(),
409-
));
405+
if let ResolvedDependency::Remote {
406+
package,
407+
registry,
408+
repository,
409+
dependants,
410+
..
411+
} = &resolved
412+
{
413+
locked.push(package.lock(registry.clone(), repository.clone(), dependants.len()));
414+
}
410415

411-
for (index, dependency) in resolved.depends_on.iter().enumerate() {
412-
let tree_char = if index + 1 == resolved.depends_on.len() {
416+
for (index, dependency) in resolved.depends_on().iter().enumerate() {
417+
let tree_char = if index + 1 == resolved.depends_on().len() {
413418
'┗'
414419
} else {
415420
'┣'

src/manifest.rs

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize};
1818
use std::{
1919
collections::HashMap,
2020
fmt::{self, Display},
21-
path::Path,
21+
path::{Path, PathBuf},
2222
str::FromStr,
2323
};
2424
use tokio::fs;
@@ -46,6 +46,8 @@ pub enum Edition {
4646
/// at any time. Users are responsible for consulting documentation and
4747
/// help channels if errors occur.
4848
Canary,
49+
/// The canary edition used by buffrs 0.8.x
50+
Canary08,
4951
/// The canary edition used by buffrs 0.7.x
5052
Canary07,
5153
/// Unknown edition of manifests
@@ -66,6 +68,7 @@ impl From<&str> for Edition {
6668
fn from(value: &str) -> Self {
6769
match value {
6870
self::CANARY_EDITION => Self::Canary,
71+
"0.8" => Self::Canary08,
6972
"0.7" => Self::Canary07,
7073
_ => Self::Unknown,
7174
}
@@ -76,6 +79,7 @@ impl From<Edition> for &'static str {
7679
fn from(value: Edition) -> Self {
7780
match value {
7881
Edition::Canary => CANARY_EDITION,
82+
Edition::Canary08 => "0.8",
7983
Edition::Canary07 => "0.7",
8084
Edition::Unknown => "unknown",
8185
}
@@ -206,7 +210,7 @@ mod deserializer {
206210
};
207211

208212
match Edition::from(edition.as_str()) {
209-
Edition::Canary | Edition::Canary07 => Ok(RawManifest::Canary {
213+
Edition::Canary | Edition::Canary08 | Edition::Canary07 => Ok(RawManifest::Canary {
210214
package,
211215
dependencies,
212216
}),
@@ -231,7 +235,7 @@ impl From<Manifest> for RawManifest {
231235
.collect();
232236

233237
match manifest.edition {
234-
Edition::Canary | Edition::Canary07 => RawManifest::Canary {
238+
Edition::Canary | Edition::Canary08 | Edition::Canary07 => RawManifest::Canary {
235239
package: manifest.package,
236240
dependencies,
237241
},
@@ -408,47 +412,92 @@ impl Dependency {
408412
) -> Self {
409413
Self {
410414
package,
411-
manifest: DependencyManifest {
415+
manifest: RemoteDependencyManifest {
412416
repository,
413417
version,
414418
registry,
415-
},
419+
}
420+
.into(),
416421
}
417422
}
418423

419424
/// Creates a copy of this dependency with a pinned version
420425
pub fn with_version(&self, version: &Version) -> Dependency {
421426
let mut dependency = self.clone();
422-
dependency.manifest.version = VersionReq {
423-
comparators: vec![semver::Comparator {
424-
op: semver::Op::Exact,
425-
major: version.major,
426-
minor: Some(version.minor),
427-
patch: Some(version.patch),
428-
pre: version.pre.clone(),
429-
}],
430-
};
427+
428+
if let DependencyManifest::Remote(ref mut manifest) = dependency.manifest {
429+
manifest.version = VersionReq {
430+
comparators: vec![semver::Comparator {
431+
op: semver::Op::Exact,
432+
major: version.major,
433+
minor: Some(version.minor),
434+
patch: Some(version.patch),
435+
pre: version.pre.clone(),
436+
}],
437+
};
438+
}
439+
431440
dependency
432441
}
433442
}
434443

435444
impl Display for Dependency {
436445
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
437-
write!(
438-
f,
439-
"{}/{}@{}",
440-
self.manifest.repository, self.package, self.manifest.version
441-
)
446+
match &self.manifest {
447+
DependencyManifest::Remote(manifest) => write!(
448+
f,
449+
"{}/{}@{}",
450+
manifest.repository, self.package, manifest.version
451+
),
452+
DependencyManifest::Local(manifest) => {
453+
write!(f, "{}@{}", self.package, manifest.path.display())
454+
}
455+
}
442456
}
443457
}
444458

445459
/// Manifest format for dependencies
446460
#[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq)]
447-
pub struct DependencyManifest {
461+
#[serde(untagged)]
462+
pub enum DependencyManifest {
463+
/// A remote dependency from artifactory
464+
Remote(RemoteDependencyManifest),
465+
/// A local dependency located on the filesystem
466+
Local(LocalDependencyManifest),
467+
}
468+
469+
impl DependencyManifest {
470+
pub(crate) fn is_local(&self) -> bool {
471+
matches!(self, DependencyManifest::Local(_))
472+
}
473+
}
474+
475+
/// Manifest format for dependencies
476+
#[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq)]
477+
pub struct RemoteDependencyManifest {
448478
/// Version requirement in the buffrs format, currently only supports pinning
449479
pub version: VersionReq,
450480
/// Artifactory repository to pull dependency from
451481
pub repository: String,
452482
/// Artifactory registry to pull from
453483
pub registry: RegistryUri,
454484
}
485+
486+
impl From<RemoteDependencyManifest> for DependencyManifest {
487+
fn from(value: RemoteDependencyManifest) -> Self {
488+
Self::Remote(value)
489+
}
490+
}
491+
492+
/// Manifest format for local filesystem dependencies
493+
#[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq)]
494+
pub struct LocalDependencyManifest {
495+
/// Path to local buffrs package
496+
pub path: PathBuf,
497+
}
498+
499+
impl From<LocalDependencyManifest> for DependencyManifest {
500+
fn from(value: LocalDependencyManifest) -> Self {
501+
Self::Local(value)
502+
}
503+
}

src/package/store.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,10 @@ impl PackageStore {
124124
pub async fn resolve(&self, package: &PackageName) -> miette::Result<Manifest> {
125125
let manifest = self.locate(package).join(MANIFEST_FILE);
126126

127-
let manifest = Manifest::try_read_from(manifest)
128-
.await?
129-
.ok_or(miette!("the package store is corrupted"))?;
127+
let manifest = Manifest::try_read_from(&manifest).await?.ok_or(miette!(
128+
"the package store is corrupted: `{}` is not present",
129+
manifest.display()
130+
))?;
130131

131132
Ok(manifest)
132133
}

0 commit comments

Comments
 (0)