Compare commits

..

14 Commits

Author SHA1 Message Date
39693774e3
feat: final commit indicating move to dagger/dagger
Some checks reported warnings
ci / deploy (push) Has been cancelled
ci-multi-platform / test (beta, ubuntu-22.04, beta) (push) Has been cancelled
ci-multi-platform / test (nightly, ubuntu-22.04, nightly) (push) Has been cancelled
ci-multi-platform / test (nightly-musl, ubuntu-22.04, nightly, x86_64-unknown-linux-musl) (push) Has been cancelled
ci-multi-platform / test (pinned, ubuntu-22.04, 1.65.0) (push) Has been cancelled
ci-multi-platform / test (stable, ubuntu-22.04, stable) (push) Has been cancelled
ci-multi-platform / test_multi_platform (macos-latest) (push) Has been cancelled
ci-multi-platform / test_multi_platform (ubuntu-latest) (push) Has been cancelled
ci-multi-platform / rustfmt (push) Has been cancelled
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-06-29 12:43:14 +02:00
107d3ca0bf 🚀 chore(README.md): check off introducing thiserror for better errors in plan for next release
The plan for the next release has been updated to reflect that the [thiserror](https://docs.rs/thiserror/latest/thiserror/) library has been introduced to improve error handling.
2023-04-30 13:12:23 +02:00
3a9abb97c2 Add thiserror instead of exposing eyre anonymous errors
The change here is to make it easier for the consumer to debug the api.
Such that they can `match` on individual errors instead of having to
parse text.

eyre is convenient, but mostly from a consumers perspective
2023-04-30 13:12:23 +02:00
66ab2f552c
🚀 chore(Cargo.toml): add thiserror crate to dependencies
The thiserror crate has been added to the dependencies in the Cargo.toml file. This crate is used to derive custom error types with automatic source location.
2023-04-30 11:41:12 +02:00
b72920e235
chore(README.md): mark dagger run compatibility and upstream update tasks as completed 2023-04-30 11:39:08 +02:00
c8bdf42e86
refactor(dagger-codegen): remove unnecessary mutability from field variable in for loop 2023-04-30 11:37:55 +02:00
40ece05140
Release dagger-core v0.2.11, dagger-sdk v0.2.22 2023-04-30 10:56:53 +02:00
2a29a66217 feat: dagger-run support 2023-04-30 00:58:59 +02:00
7d186c477d fix: remove _ from _type 2023-04-30 00:41:48 +02:00
cf92018a1e feat: set cache config in proper place 2023-04-30 00:41:48 +02:00
09fd7c89b8 feat: add inline export of variable 2023-04-30 00:41:48 +02:00
e39ee28ec0 feat: expose runtime 2023-04-30 00:41:48 +02:00
4dac924783 chore: update ci 2023-04-30 00:41:48 +02:00
eb7470c604 feat: update to dagger-5.1 2023-04-30 00:41:48 +02:00
24 changed files with 540 additions and 179 deletions

View File

@ -8,7 +8,6 @@ env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
_EXPERIMENTAL_DAGGER_CACHE_CONFIG: type=gha;mode=max
jobs: jobs:
deploy: deploy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -28,5 +27,10 @@ jobs:
uses: docker/setup-qemu-action@v2 uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.0.0 uses: docker/setup-buildx-action@v2.0.0
-
name: Expose GitHub Runtime
uses: crazy-max/ghaction-github-runtime@v2
- name: Run dagger [CI] - name: Run dagger [CI]
run: cargo run -p ci -- pr run: |
export _EXPERIMENTAL_DAGGER_CACHE_CONFIG="type=gha,mode=max,url=$ACTIONS_CACHE_URL,token=$ACTIONS_RUNTIME_TOKEN"
cargo run -p ci -- pr

6
Cargo.lock generated
View File

@ -317,7 +317,7 @@ dependencies = [
[[package]] [[package]]
name = "dagger-core" name = "dagger-core"
version = "0.2.10" version = "0.2.11"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"base64", "base64",
@ -334,6 +334,7 @@ dependencies = [
"sha2", "sha2",
"tar", "tar",
"tempfile", "tempfile",
"thiserror",
"tokio", "tokio",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
@ -341,7 +342,7 @@ dependencies = [
[[package]] [[package]]
name = "dagger-sdk" name = "dagger-sdk"
version = "0.2.21" version = "0.2.22"
dependencies = [ dependencies = [
"dagger-core", "dagger-core",
"derive_builder", "derive_builder",
@ -352,6 +353,7 @@ dependencies = [
"rand", "rand",
"serde", "serde",
"serde_json", "serde_json",
"thiserror",
"tokio", "tokio",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",

View File

@ -23,3 +23,4 @@ tracing-subscriber = { version = "0.3.16", features = [
"tracing-log", "tracing-log",
"tracing", "tracing",
] } ] }
thiserror = "1.0.40"

View File

@ -1,69 +1,7 @@
# dagger-sdk # dagger-sdk
A dagger sdk written in rust for rust. A dagger sdk written in rust
## Plan for next release ## Disclaimer:
- [ ] Introduce [thiserror](https://docs.rs/thiserror/latest/thiserror/) for Repository has moved to: https://github.com/dagger/dagger/tree/main/sdk/rust
better errors
- [ ] Add compatibility with `dagger run`
- [ ] Add open telemetry tracing to the sdk
- [ ] Remove `id().await?` from passing to other dagger graphs, this should make
the design much cleaner
- [ ] Start MkBook on how to actually use the sdk
- [ ] Update to newest upstream release
- [ ] Fix bugs
- [x] Run in conjunction with golang and other sdks
- [ ] Stabilize the initial `Arc<Query>` model into something more extensible
## Examples
See [examples](./crates/dagger-sdk/examples/)
Run them like so
```bash
cargo run --example first-pipeline
```
The examples match the folder name in each directory in examples
## Install
Simply install like:
```bash
cargo add dagger-sdk
```
### Usage
```rust
#[tokio::main]
async fn main() -> eyre::Result<()> {
let client = dagger_sdk::connect().await?;
let version = client
.container()
.from("golang:1.19")
.with_exec(vec!["go", "version"])
.stdout()
.await?;
println!("Hello from Dagger and {}", version.trim());
Ok(())
}
```
And run it like a normal application:
```bash
cargo run
```
### Contributing
See [CONTRIBUTING](./CONTRIBUTING.md)
or just cargo make codegen

View File

@ -8,6 +8,6 @@ edition = "2021"
[dependencies] [dependencies]
clap = "4.1.6" clap = "4.1.6"
color-eyre = "0.6.2" color-eyre = "0.6.2"
dagger-sdk = { path = "../crates/dagger-sdk/", version = "^0.2.21" } dagger-sdk = { path = "../crates/dagger-sdk/", version = "^0.2.22" }
eyre = "0.6.8" eyre = "0.6.8"
tokio = { version = "1.25.0", features = ["full"] } tokio = { version = "1.25.0", features = ["full"] }

View File

@ -13,7 +13,7 @@ use self::generator::DynGenerator;
fn set_schema_parents(mut schema: Schema) -> Schema { fn set_schema_parents(mut schema: Schema) -> Schema {
for t in schema.types.as_mut().into_iter().flatten().flatten() { for t in schema.types.as_mut().into_iter().flatten().flatten() {
let t_parent = t.full_type.clone(); let t_parent = t.full_type.clone();
for mut field in t.full_type.fields.as_mut().into_iter().flatten() { for field in t.full_type.fields.as_mut().into_iter().flatten() {
field.parent_type = Some(t_parent.clone()); field.parent_type = Some(t_parent.clone());
} }
} }

View File

@ -235,8 +235,10 @@ fn render_output_type(funcs: &CommonFunctions, type_ref: &TypeRef) -> rust::Toke
}; };
} }
let dagger_error = rust::import("crate::errors", "DaggerError");
quote! { quote! {
eyre::Result<$output_type> Result<$output_type, $dagger_error>
} }
} }

View File

@ -18,7 +18,7 @@ pub fn render_object(funcs: &CommonFunctions, t: &FullType) -> eyre::Result<rust
Ok(quote! { Ok(quote! {
#[derive(Clone)] #[derive(Clone)]
pub struct $(t.name.pipe(|s| format_name(s))) { pub struct $(t.name.pipe(|s| format_name(s))) {
pub proc: $arc<$child>, pub proc: Option<$arc<$child>>,
pub selection: $selection, pub selection: $selection,
pub graphql_client: $graphql_client pub graphql_client: $graphql_client
} }

View File

@ -5,6 +5,33 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## v0.2.11 (2023-04-29)
### New Features
- <csr-id-2a29a66217fa4d6c530ea1ce670c8836383e7051/> dagger-run support
- <csr-id-eb7470c604169d1a15976078c0889d5cc7011257/> update to dagger-5.1
### Commit Statistics
<csr-read-only-do-not-edit/>
- 2 commits contributed to the release.
- 4 days passed between releases.
- 2 commits were understood as [conventional](https://www.conventionalcommits.org).
- 0 issues like '(#ID)' were seen in commit messages
### Commit Details
<csr-read-only-do-not-edit/>
<details><summary>view details</summary>
* **Uncategorized**
- dagger-run support ([`2a29a66`](https://github.com/kjuulh/dagger-sdk/commit/2a29a66217fa4d6c530ea1ce670c8836383e7051))
- update to dagger-5.1 ([`eb7470c`](https://github.com/kjuulh/dagger-sdk/commit/eb7470c604169d1a15976078c0889d5cc7011257))
</details>
## v0.2.10 (2023-04-25) ## v0.2.10 (2023-04-25)
### Bug Fixes ### Bug Fixes
@ -15,7 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
<csr-read-only-do-not-edit/> <csr-read-only-do-not-edit/>
- 1 commit contributed to the release. - 2 commits contributed to the release.
- 21 days passed between releases. - 21 days passed between releases.
- 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 commit was understood as [conventional](https://www.conventionalcommits.org).
- 0 issues like '(#ID)' were seen in commit messages - 0 issues like '(#ID)' were seen in commit messages
@ -27,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
<details><summary>view details</summary> <details><summary>view details</summary>
* **Uncategorized** * **Uncategorized**
- Release dagger-core v0.2.10 ([`8011c42`](https://github.com/kjuulh/dagger-sdk/commit/8011c42dc077d101b1bccaf231fac17636dd249d))
- delete other files/folder in downloads: #57 ([`9d3c21d`](https://github.com/kjuulh/dagger-sdk/commit/9d3c21d16b4a64eb7a7b1888365a4c4ea56d7225)) - delete other files/folder in downloads: #57 ([`9d3c21d`](https://github.com/kjuulh/dagger-sdk/commit/9d3c21d16b4a64eb7a7b1888365a4c4ea56d7225))
</details> </details>

View File

@ -1,6 +1,6 @@
[package] [package]
name = "dagger-core" name = "dagger-core"
version = "0.2.10" version = "0.2.11"
edition = "2021" edition = "2021"
readme = "README.md" readme = "README.md"
license-file = "LICENSE.MIT" license-file = "LICENSE.MIT"
@ -16,6 +16,7 @@ serde_json = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
tracing-subscriber = { workspace = true } tracing-subscriber = { workspace = true }
thiserror.workspace = true
base64 = "0.21.0" base64 = "0.21.0"
dirs = "4.0.0" dirs = "4.0.0"

View File

@ -89,6 +89,7 @@ impl InnerCliSession {
while let Ok(Some(line)) = stdout_bufr.next_line().await { while let Ok(Some(line)) = stdout_bufr.next_line().await {
if let Ok(conn) = serde_json::from_str::<ConnectParams>(&line) { if let Ok(conn) = serde_json::from_str::<ConnectParams>(&line) {
sender.send(conn).await.unwrap(); sender.send(conn).await.unwrap();
continue;
} }
if let Some(logger) = &logger { if let Some(logger) = &logger {

View File

@ -23,10 +23,25 @@ impl Engine {
pub async fn start( pub async fn start(
&self, &self,
cfg: &Config, cfg: &Config,
) -> eyre::Result<(ConnectParams, tokio::process::Child)> { ) -> eyre::Result<(ConnectParams, Option<tokio::process::Child>)> {
tracing::info!("starting dagger-engine"); tracing::info!("starting dagger-engine");
// TODO: Add from existing session as well if let Ok(conn) = self.from_session_env().await {
self.from_cli(cfg).await return Ok((conn, None));
}
let (conn, proc) = self.from_cli(cfg).await?;
Ok((conn, Some(proc)))
}
async fn from_session_env(&self) -> eyre::Result<ConnectParams> {
let port = std::env::var("DAGGER_SESSION_PORT").map(|p| p.parse::<u64>())??;
let token = std::env::var("DAGGER_SESSION_TOKEN")?;
Ok(ConnectParams {
port,
session_token: token,
})
} }
} }

View File

@ -15,7 +15,7 @@ pub struct GraphQLError {
#[derive(Deserialize, Debug, Clone)] #[derive(Deserialize, Debug, Clone)]
#[allow(dead_code)] #[allow(dead_code)]
pub struct GraphQLErrorMessage { pub struct GraphQLErrorMessage {
message: String, pub message: String,
locations: Option<Vec<GraphQLErrorLocation>>, locations: Option<Vec<GraphQLErrorLocation>>,
extensions: Option<HashMap<String, String>>, extensions: Option<HashMap<String, String>>,
path: Option<Vec<GraphQLErrorPathParam>>, path: Option<Vec<GraphQLErrorPathParam>>,

View File

@ -4,13 +4,14 @@ use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use base64::engine::general_purpose; use base64::engine::general_purpose;
use base64::Engine; use base64::Engine;
use thiserror::Error;
use crate::connect_params::ConnectParams; use crate::connect_params::ConnectParams;
use crate::gql_client::{ClientConfig, GQLClient}; use crate::gql_client::{ClientConfig, GQLClient};
#[async_trait] #[async_trait]
pub trait GraphQLClient { pub trait GraphQLClient {
async fn query(&self, query: &str) -> eyre::Result<Option<serde_json::Value>>; async fn query(&self, query: &str) -> Result<Option<serde_json::Value>, GraphQLError>;
} }
pub type DynGraphQLClient = Arc<dyn GraphQLClient + Send + Sync>; pub type DynGraphQLClient = Arc<dyn GraphQLClient + Send + Sync>;
@ -40,13 +41,50 @@ impl DefaultGraphQLClient {
#[async_trait] #[async_trait]
impl GraphQLClient for DefaultGraphQLClient { impl GraphQLClient for DefaultGraphQLClient {
async fn query(&self, query: &str) -> eyre::Result<Option<serde_json::Value>> { async fn query(&self, query: &str) -> Result<Option<serde_json::Value>, GraphQLError> {
let res: Option<serde_json::Value> = self let res: Option<serde_json::Value> =
.client self.client.query(&query).await.map_err(map_graphql_error)?;
.query(&query)
.await
.map_err(|r| eyre::anyhow!(r.to_string()))?;
return Ok(res); return Ok(res);
} }
} }
fn map_graphql_error(gql_error: crate::gql_client::GraphQLError) -> GraphQLError {
let message = gql_error.message().to_string();
let json = gql_error.json();
if let Some(json) = json {
if !json.is_empty() {
return GraphQLError::DomainError {
message,
fields: GraphqlErrorMessages(json.into_iter().map(|e| e.message).collect()),
};
}
}
GraphQLError::HttpError(message)
}
#[derive(Error, Debug)]
pub enum GraphQLError {
#[error("http error: {0}")]
HttpError(String),
#[error("domain error:\n{message}\n{fields}")]
DomainError {
message: String,
fields: GraphqlErrorMessages,
},
}
#[derive(Debug, Clone)]
pub struct GraphqlErrorMessages(Vec<String>);
impl std::fmt::Display for GraphqlErrorMessages {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for error in self.0.iter() {
f.write_fmt(format_args!("{error}\n"))?;
}
Ok(())
}
}

View File

@ -1,6 +1,6 @@
#![deny(warnings)] #![deny(warnings)]
pub const DAGGER_ENGINE_VERSION: &'static str = "0.4.2"; pub const DAGGER_ENGINE_VERSION: &'static str = "0.5.1";
pub mod cli_session; pub mod cli_session;
pub mod config; pub mod config;

View File

@ -6,8 +6,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html). [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## v0.2.22 (2023-04-29)
### New Features
- <csr-id-2a29a66217fa4d6c530ea1ce670c8836383e7051/> dagger-run support
- <csr-id-eb7470c604169d1a15976078c0889d5cc7011257/> update to dagger-5.1
### Commit Statistics
<csr-read-only-do-not-edit/>
- 2 commits contributed to the release.
- 4 days passed between releases.
- 2 commits were understood as [conventional](https://www.conventionalcommits.org).
- 0 issues like '(#ID)' were seen in commit messages
### Commit Details
<csr-read-only-do-not-edit/>
<details><summary>view details</summary>
* **Uncategorized**
- dagger-run support ([`2a29a66`](https://github.com/kjuulh/dagger-sdk/commit/2a29a66217fa4d6c530ea1ce670c8836383e7051))
- update to dagger-5.1 ([`eb7470c`](https://github.com/kjuulh/dagger-sdk/commit/eb7470c604169d1a15976078c0889d5cc7011257))
</details>
## v0.2.21 (2023-04-25) ## v0.2.21 (2023-04-25)
<csr-id-09881ee39bdfb9201d104e4679a51c3b76b5fe27/>
### Chore ### Chore
- <csr-id-09881ee39bdfb9201d104e4679a51c3b76b5fe27/> add new dagger-core-version - <csr-id-09881ee39bdfb9201d104e4679a51c3b76b5fe27/> add new dagger-core-version
@ -16,7 +45,7 @@ and this project adheres to
<csr-read-only-do-not-edit/> <csr-read-only-do-not-edit/>
- 1 commit contributed to the release. - 2 commits contributed to the release.
- 21 days passed between releases. - 21 days passed between releases.
- 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 commit was understood as [conventional](https://www.conventionalcommits.org).
- 0 issues like '(#ID)' were seen in commit messages - 0 issues like '(#ID)' were seen in commit messages
@ -28,6 +57,7 @@ and this project adheres to
<details><summary>view details</summary> <details><summary>view details</summary>
* **Uncategorized** * **Uncategorized**
- Release dagger-sdk v0.2.21 ([`6937ef0`](https://github.com/kjuulh/dagger-sdk/commit/6937ef0ace797315013513aa7e2af39a9206a738))
- add new dagger-core-version ([`09881ee`](https://github.com/kjuulh/dagger-sdk/commit/09881ee39bdfb9201d104e4679a51c3b76b5fe27)) - add new dagger-core-version ([`09881ee`](https://github.com/kjuulh/dagger-sdk/commit/09881ee39bdfb9201d104e4679a51c3b76b5fe27))
</details> </details>

View File

@ -1,6 +1,6 @@
[package] [package]
name = "dagger-sdk" name = "dagger-sdk"
version = "0.2.21" version = "0.2.22"
edition = "2021" edition = "2021"
readme = "README.md" readme = "README.md"
license-file = "LICENSE.MIT" license-file = "LICENSE.MIT"
@ -11,7 +11,7 @@ publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
dagger-core = { workspace = true, version = "0.2.10" } dagger-core = { workspace = true, version = "^0.2.11" }
eyre = { workspace = true } eyre = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
@ -19,6 +19,7 @@ serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
tracing.workspace = true tracing.workspace = true
tracing-subscriber.workspace = true tracing-subscriber.workspace = true
thiserror.workspace = true
futures = "0.3.28" futures = "0.3.28"
derive_builder = "0.12.0" derive_builder = "0.12.0"

View File

@ -7,7 +7,7 @@ async fn main() -> eyre::Result<()> {
let host_source_dir = client.host().directory_opts( let host_source_dir = client.host().directory_opts(
"./examples/caching/app", "./examples/caching/app",
dagger_sdk::HostDirectoryOptsBuilder::default() dagger_sdk::HostDirectoryOptsBuilder::default()
.exclude(vec!["node_modules", "ci/"]) .exclude(vec!["node_modules/", "ci/"])
.build()?, .build()?,
); );

View File

@ -5,23 +5,27 @@ use dagger_core::graphql_client::DefaultGraphQLClient;
use dagger_core::config::Config; use dagger_core::config::Config;
use dagger_core::engine::Engine as DaggerEngine; use dagger_core::engine::Engine as DaggerEngine;
use crate::errors::ConnectError;
use crate::gen::Query; use crate::gen::Query;
use crate::logging::StdLogger; use crate::logging::StdLogger;
use crate::querybuilder::query; use crate::querybuilder::query;
pub type DaggerConn = Arc<Query>; pub type DaggerConn = Arc<Query>;
pub async fn connect() -> eyre::Result<DaggerConn> { pub async fn connect() -> Result<DaggerConn, ConnectError> {
let cfg = Config::new(None, None, None, None, Some(Arc::new(StdLogger::default()))); let cfg = Config::new(None, None, None, None, Some(Arc::new(StdLogger::default())));
connect_opts(cfg).await connect_opts(cfg).await
} }
pub async fn connect_opts(cfg: Config) -> eyre::Result<DaggerConn> { pub async fn connect_opts(cfg: Config) -> Result<DaggerConn, ConnectError> {
let (conn, proc) = DaggerEngine::new().start(&cfg).await?; let (conn, proc) = DaggerEngine::new()
.start(&cfg)
.await
.map_err(ConnectError::FailedToConnect)?;
Ok(Arc::new(Query { Ok(Arc::new(Query {
proc: Arc::new(proc), proc: proc.map(|p| Arc::new(p)),
selection: query(), selection: query(),
graphql_client: Arc::new(DefaultGraphQLClient::new(&conn)), graphql_client: Arc::new(DefaultGraphQLClient::new(&conn)),
})) }))

View File

@ -0,0 +1,27 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ConnectError {
#[error("failed to connect to dagger engine")]
FailedToConnect(#[source] eyre::Error),
}
#[derive(Error, Debug)]
pub enum DaggerError {
#[error("failed to build dagger internal graph")]
Build(#[source] eyre::Error),
#[error("failed to parse input type")]
Serialize(#[source] eyre::Error),
#[error("failed to query dagger engine: {0}")]
Query(#[source] dagger_core::graphql_client::GraphQLError),
#[error("failed to unpack response")]
Unpack(#[source] DaggerUnpackError),
}
#[derive(Error, Debug)]
pub enum DaggerUnpackError {
#[error("Too many nested objects inside graphql response")]
TooManyNestedObjects,
#[error("failed to deserialize response")]
Deserialize(#[source] serde_json::Error),
}

View File

@ -1,3 +1,4 @@
use crate::errors::DaggerError;
use crate::querybuilder::Selection; use crate::querybuilder::Selection;
use dagger_core::graphql_client::DynGraphQLClient; use dagger_core::graphql_client::DynGraphQLClient;
use derive_builder::Builder; use derive_builder::Builder;
@ -115,13 +116,13 @@ pub struct PipelineLabel {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct CacheVolume { pub struct CacheVolume {
pub proc: Arc<Child>, pub proc: Option<Arc<Child>>,
pub selection: Selection, pub selection: Selection,
pub graphql_client: DynGraphQLClient, pub graphql_client: DynGraphQLClient,
} }
impl CacheVolume { impl CacheVolume {
pub async fn id(&self) -> eyre::Result<CacheId> { pub async fn id(&self) -> Result<CacheId, DaggerError> {
let query = self.selection.select("id"); let query = self.selection.select("id");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -129,7 +130,7 @@ impl CacheVolume {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Container { pub struct Container {
pub proc: Arc<Child>, pub proc: Option<Arc<Child>>,
pub selection: Selection, pub selection: Selection,
pub graphql_client: DynGraphQLClient, pub graphql_client: DynGraphQLClient,
} }
@ -143,6 +144,10 @@ pub struct ContainerBuildOpts<'a> {
/// Default: './Dockerfile'. /// Default: './Dockerfile'.
#[builder(setter(into, strip_option), default)] #[builder(setter(into, strip_option), default)]
pub dockerfile: Option<&'a str>, pub dockerfile: Option<&'a str>,
/// Secrets to pass to the build.
/// They will be mounted at /run/secrets/[secret-name].
#[builder(setter(into, strip_option), default)]
pub secrets: Option<Vec<SecretId>>,
/// Target build stage to build. /// Target build stage to build.
#[builder(setter(into, strip_option), default)] #[builder(setter(into, strip_option), default)]
pub target: Option<&'a str>, pub target: Option<&'a str>,
@ -184,6 +189,13 @@ pub struct ContainerExportOpts {
pub platform_variants: Option<Vec<ContainerId>>, pub platform_variants: Option<Vec<ContainerId>>,
} }
#[derive(Builder, Debug, PartialEq)] #[derive(Builder, Debug, PartialEq)]
pub struct ContainerImportOpts<'a> {
/// Identifies the tag to import from the archive, if the archive bundles
/// multiple tags.
#[builder(setter(into, strip_option), default)]
pub tag: Option<&'a str>,
}
#[derive(Builder, Debug, PartialEq)]
pub struct ContainerPipelineOpts<'a> { pub struct ContainerPipelineOpts<'a> {
/// Pipeline description. /// Pipeline description.
#[builder(setter(into, strip_option), default)] #[builder(setter(into, strip_option), default)]
@ -213,6 +225,11 @@ pub struct ContainerWithDirectoryOpts<'a> {
/// Patterns to include in the written directory (e.g., ["*.go", "go.mod", "go.sum"]). /// Patterns to include in the written directory (e.g., ["*.go", "go.mod", "go.sum"]).
#[builder(setter(into, strip_option), default)] #[builder(setter(into, strip_option), default)]
pub include: Option<Vec<&'a str>>, pub include: Option<Vec<&'a str>>,
/// A user:group to set for the directory and its contents.
/// The user and group can either be an ID (1000:1000) or a name (foo:bar).
/// If the group is omitted, it defaults to the same as the user.
#[builder(setter(into, strip_option), default)]
pub owner: Option<&'a str>,
} }
#[derive(Builder, Debug, PartialEq)] #[derive(Builder, Debug, PartialEq)]
pub struct ContainerWithExecOpts<'a> { pub struct ContainerWithExecOpts<'a> {
@ -233,6 +250,9 @@ pub struct ContainerWithExecOpts<'a> {
/// Redirect the command's standard output to a file in the container (e.g., "/tmp/stdout"). /// Redirect the command's standard output to a file in the container (e.g., "/tmp/stdout").
#[builder(setter(into, strip_option), default)] #[builder(setter(into, strip_option), default)]
pub redirect_stdout: Option<&'a str>, pub redirect_stdout: Option<&'a str>,
/// If the container has an entrypoint, ignore it for args rather than using it to wrap them.
#[builder(setter(into, strip_option), default)]
pub skip_entrypoint: Option<bool>,
/// Content to write to the command's standard input before closing (e.g., "Hello world"). /// Content to write to the command's standard input before closing (e.g., "Hello world").
#[builder(setter(into, strip_option), default)] #[builder(setter(into, strip_option), default)]
pub stdin: Option<&'a str>, pub stdin: Option<&'a str>,
@ -247,14 +267,27 @@ pub struct ContainerWithExposedPortOpts<'a> {
pub protocol: Option<NetworkProtocol>, pub protocol: Option<NetworkProtocol>,
} }
#[derive(Builder, Debug, PartialEq)] #[derive(Builder, Debug, PartialEq)]
pub struct ContainerWithFileOpts { pub struct ContainerWithFileOpts<'a> {
/// A user:group to set for the file.
/// The user and group can either be an ID (1000:1000) or a name (foo:bar).
/// If the group is omitted, it defaults to the same as the user.
#[builder(setter(into, strip_option), default)]
pub owner: Option<&'a str>,
/// Permission given to the copied file (e.g., 0600). /// Permission given to the copied file (e.g., 0600).
/// Default: 0644. /// Default: 0644.
#[builder(setter(into, strip_option), default)] #[builder(setter(into, strip_option), default)]
pub permissions: Option<isize>, pub permissions: Option<isize>,
} }
#[derive(Builder, Debug, PartialEq)] #[derive(Builder, Debug, PartialEq)]
pub struct ContainerWithMountedCacheOpts { pub struct ContainerWithMountedCacheOpts<'a> {
/// A user:group to set for the mounted cache directory.
/// Note that this changes the ownership of the specified mount along with the
/// initial filesystem provided by source (if any). It does not have any effect
/// if/when the cache has already been created.
/// The user and group can either be an ID (1000:1000) or a name (foo:bar).
/// If the group is omitted, it defaults to the same as the user.
#[builder(setter(into, strip_option), default)]
pub owner: Option<&'a str>,
/// Sharing mode of the cache volume. /// Sharing mode of the cache volume.
#[builder(setter(into, strip_option), default)] #[builder(setter(into, strip_option), default)]
pub sharing: Option<CacheSharingMode>, pub sharing: Option<CacheSharingMode>,
@ -263,16 +296,53 @@ pub struct ContainerWithMountedCacheOpts {
pub source: Option<DirectoryId>, pub source: Option<DirectoryId>,
} }
#[derive(Builder, Debug, PartialEq)] #[derive(Builder, Debug, PartialEq)]
pub struct ContainerWithMountedDirectoryOpts<'a> {
/// A user:group to set for the mounted directory and its contents.
/// The user and group can either be an ID (1000:1000) or a name (foo:bar).
/// If the group is omitted, it defaults to the same as the user.
#[builder(setter(into, strip_option), default)]
pub owner: Option<&'a str>,
}
#[derive(Builder, Debug, PartialEq)]
pub struct ContainerWithMountedFileOpts<'a> {
/// A user or user:group to set for the mounted file.
/// The user and group can either be an ID (1000:1000) or a name (foo:bar).
/// If the group is omitted, it defaults to the same as the user.
#[builder(setter(into, strip_option), default)]
pub owner: Option<&'a str>,
}
#[derive(Builder, Debug, PartialEq)]
pub struct ContainerWithMountedSecretOpts<'a> {
/// A user:group to set for the mounted secret.
/// The user and group can either be an ID (1000:1000) or a name (foo:bar).
/// If the group is omitted, it defaults to the same as the user.
#[builder(setter(into, strip_option), default)]
pub owner: Option<&'a str>,
}
#[derive(Builder, Debug, PartialEq)]
pub struct ContainerWithNewFileOpts<'a> { pub struct ContainerWithNewFileOpts<'a> {
/// Content of the file to write (e.g., "Hello world!"). /// Content of the file to write (e.g., "Hello world!").
#[builder(setter(into, strip_option), default)] #[builder(setter(into, strip_option), default)]
pub contents: Option<&'a str>, pub contents: Option<&'a str>,
/// A user:group to set for the file.
/// The user and group can either be an ID (1000:1000) or a name (foo:bar).
/// If the group is omitted, it defaults to the same as the user.
#[builder(setter(into, strip_option), default)]
pub owner: Option<&'a str>,
/// Permission given to the written file (e.g., 0600). /// Permission given to the written file (e.g., 0600).
/// Default: 0644. /// Default: 0644.
#[builder(setter(into, strip_option), default)] #[builder(setter(into, strip_option), default)]
pub permissions: Option<isize>, pub permissions: Option<isize>,
} }
#[derive(Builder, Debug, PartialEq)] #[derive(Builder, Debug, PartialEq)]
pub struct ContainerWithUnixSocketOpts<'a> {
/// A user:group to set for the mounted socket.
/// The user and group can either be an ID (1000:1000) or a name (foo:bar).
/// If the group is omitted, it defaults to the same as the user.
#[builder(setter(into, strip_option), default)]
pub owner: Option<&'a str>,
}
#[derive(Builder, Debug, PartialEq)]
pub struct ContainerWithoutExposedPortOpts { pub struct ContainerWithoutExposedPortOpts {
/// Port protocol to unexpose /// Port protocol to unexpose
#[builder(setter(into, strip_option), default)] #[builder(setter(into, strip_option), default)]
@ -317,6 +387,9 @@ impl Container {
if let Some(target) = opts.target { if let Some(target) = opts.target {
query = query.arg("target", target); query = query.arg("target", target);
} }
if let Some(secrets) = opts.secrets {
query = query.arg("secrets", secrets);
}
return Container { return Container {
proc: self.proc.clone(), proc: self.proc.clone(),
@ -325,7 +398,7 @@ impl Container {
}; };
} }
/// Retrieves default arguments for future commands. /// Retrieves default arguments for future commands.
pub async fn default_args(&self) -> eyre::Result<Vec<String>> { pub async fn default_args(&self) -> Result<Vec<String>, DaggerError> {
let query = self.selection.select("defaultArgs"); let query = self.selection.select("defaultArgs");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -355,7 +428,7 @@ impl Container {
/// # Arguments /// # Arguments
/// ///
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use /// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub async fn endpoint(&self) -> eyre::Result<String> { pub async fn endpoint(&self) -> Result<String, DaggerError> {
let query = self.selection.select("endpoint"); let query = self.selection.select("endpoint");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -369,7 +442,10 @@ impl Container {
/// # Arguments /// # Arguments
/// ///
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use /// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub async fn endpoint_opts<'a>(&self, opts: ContainerEndpointOpts<'a>) -> eyre::Result<String> { pub async fn endpoint_opts<'a>(
&self,
opts: ContainerEndpointOpts<'a>,
) -> Result<String, DaggerError> {
let mut query = self.selection.select("endpoint"); let mut query = self.selection.select("endpoint");
if let Some(port) = opts.port { if let Some(port) = opts.port {
@ -382,7 +458,7 @@ impl Container {
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// Retrieves entrypoint to be prepended to the arguments of all commands. /// Retrieves entrypoint to be prepended to the arguments of all commands.
pub async fn entrypoint(&self) -> eyre::Result<Vec<String>> { pub async fn entrypoint(&self) -> Result<Vec<String>, DaggerError> {
let query = self.selection.select("entrypoint"); let query = self.selection.select("entrypoint");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -392,7 +468,7 @@ impl Container {
/// # Arguments /// # Arguments
/// ///
/// * `name` - The name of the environment variable to retrieve (e.g., "PATH"). /// * `name` - The name of the environment variable to retrieve (e.g., "PATH").
pub async fn env_variable(&self, name: impl Into<String>) -> eyre::Result<String> { pub async fn env_variable(&self, name: impl Into<String>) -> Result<String, DaggerError> {
let mut query = self.selection.select("envVariable"); let mut query = self.selection.select("envVariable");
query = query.arg("name", name.into()); query = query.arg("name", name.into());
@ -458,8 +534,8 @@ impl Container {
}; };
} }
/// Exit code of the last executed command. Zero means success. /// Exit code of the last executed command. Zero means success.
/// Errors if no command has been executed. /// Will execute default command if none is set, or error if there's no default.
pub async fn exit_code(&self) -> eyre::Result<isize> { pub async fn exit_code(&self) -> Result<isize, DaggerError> {
let query = self.selection.select("exitCode"); let query = self.selection.select("exitCode");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -473,7 +549,7 @@ impl Container {
/// * `path` - Host's destination path (e.g., "./tarball"). /// * `path` - Host's destination path (e.g., "./tarball").
/// Path can be relative to the engine's workdir or absolute. /// Path can be relative to the engine's workdir or absolute.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use /// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub async fn export(&self, path: impl Into<String>) -> eyre::Result<bool> { pub async fn export(&self, path: impl Into<String>) -> Result<bool, DaggerError> {
let mut query = self.selection.select("export"); let mut query = self.selection.select("export");
query = query.arg("path", path.into()); query = query.arg("path", path.into());
@ -494,7 +570,7 @@ impl Container {
&self, &self,
path: impl Into<String>, path: impl Into<String>,
opts: ContainerExportOpts, opts: ContainerExportOpts,
) -> eyre::Result<bool> { ) -> Result<bool, DaggerError> {
let mut query = self.selection.select("export"); let mut query = self.selection.select("export");
query = query.arg("path", path.into()); query = query.arg("path", path.into());
@ -562,25 +638,67 @@ impl Container {
} }
/// Retrieves a hostname which can be used by clients to reach this container. /// Retrieves a hostname which can be used by clients to reach this container.
/// Currently experimental; set _EXPERIMENTAL_DAGGER_SERVICES_DNS=0 to disable. /// Currently experimental; set _EXPERIMENTAL_DAGGER_SERVICES_DNS=0 to disable.
pub async fn hostname(&self) -> eyre::Result<String> { pub async fn hostname(&self) -> Result<String, DaggerError> {
let query = self.selection.select("hostname"); let query = self.selection.select("hostname");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// A unique identifier for this container. /// A unique identifier for this container.
pub async fn id(&self) -> eyre::Result<ContainerId> { pub async fn id(&self) -> Result<ContainerId, DaggerError> {
let query = self.selection.select("id"); let query = self.selection.select("id");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// The unique image reference which can only be retrieved immediately after the 'Container.From' call. /// The unique image reference which can only be retrieved immediately after the 'Container.From' call.
pub async fn image_ref(&self) -> eyre::Result<String> { pub async fn image_ref(&self) -> Result<String, DaggerError> {
let query = self.selection.select("imageRef"); let query = self.selection.select("imageRef");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// Reads the container from an OCI tarball.
/// NOTE: this involves unpacking the tarball to an OCI store on the host at
/// $XDG_CACHE_DIR/dagger/oci. This directory can be removed whenever you like.
///
/// # Arguments
///
/// * `source` - File to read the container from.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub fn import(&self, source: FileId) -> Container {
let mut query = self.selection.select("import");
query = query.arg("source", source);
return Container {
proc: self.proc.clone(),
selection: query,
graphql_client: self.graphql_client.clone(),
};
}
/// Reads the container from an OCI tarball.
/// NOTE: this involves unpacking the tarball to an OCI store on the host at
/// $XDG_CACHE_DIR/dagger/oci. This directory can be removed whenever you like.
///
/// # Arguments
///
/// * `source` - File to read the container from.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub fn import_opts<'a>(&self, source: FileId, opts: ContainerImportOpts<'a>) -> Container {
let mut query = self.selection.select("import");
query = query.arg("source", source);
if let Some(tag) = opts.tag {
query = query.arg("tag", tag);
}
return Container {
proc: self.proc.clone(),
selection: query,
graphql_client: self.graphql_client.clone(),
};
}
/// Retrieves the value of the specified label. /// Retrieves the value of the specified label.
pub async fn label(&self, name: impl Into<String>) -> eyre::Result<String> { pub async fn label(&self, name: impl Into<String>) -> Result<String, DaggerError> {
let mut query = self.selection.select("label"); let mut query = self.selection.select("label");
query = query.arg("name", name.into()); query = query.arg("name", name.into());
@ -598,7 +716,7 @@ impl Container {
}]; }];
} }
/// Retrieves the list of paths where a directory is mounted. /// Retrieves the list of paths where a directory is mounted.
pub async fn mounts(&self) -> eyre::Result<Vec<String>> { pub async fn mounts(&self) -> Result<Vec<String>, DaggerError> {
let query = self.selection.select("mounts"); let query = self.selection.select("mounts");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -649,7 +767,7 @@ impl Container {
}; };
} }
/// The platform this container executes and publishes as. /// The platform this container executes and publishes as.
pub async fn platform(&self) -> eyre::Result<Platform> { pub async fn platform(&self) -> Result<Platform, DaggerError> {
let query = self.selection.select("platform"); let query = self.selection.select("platform");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -664,7 +782,7 @@ impl Container {
/// ///
/// Formatted as [host]/[user]/[repo]:[tag] (e.g. "docker.io/dagger/dagger:main"). /// Formatted as [host]/[user]/[repo]:[tag] (e.g. "docker.io/dagger/dagger:main").
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use /// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub async fn publish(&self, address: impl Into<String>) -> eyre::Result<String> { pub async fn publish(&self, address: impl Into<String>) -> Result<String, DaggerError> {
let mut query = self.selection.select("publish"); let mut query = self.selection.select("publish");
query = query.arg("address", address.into()); query = query.arg("address", address.into());
@ -686,7 +804,7 @@ impl Container {
&self, &self,
address: impl Into<String>, address: impl Into<String>,
opts: ContainerPublishOpts, opts: ContainerPublishOpts,
) -> eyre::Result<String> { ) -> Result<String, DaggerError> {
let mut query = self.selection.select("publish"); let mut query = self.selection.select("publish");
query = query.arg("address", address.into()); query = query.arg("address", address.into());
@ -707,21 +825,21 @@ impl Container {
}; };
} }
/// The error stream of the last executed command. /// The error stream of the last executed command.
/// Errors if no command has been executed. /// Will execute default command if none is set, or error if there's no default.
pub async fn stderr(&self) -> eyre::Result<String> { pub async fn stderr(&self) -> Result<String, DaggerError> {
let query = self.selection.select("stderr"); let query = self.selection.select("stderr");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// The output stream of the last executed command. /// The output stream of the last executed command.
/// Errors if no command has been executed. /// Will execute default command if none is set, or error if there's no default.
pub async fn stdout(&self) -> eyre::Result<String> { pub async fn stdout(&self) -> Result<String, DaggerError> {
let query = self.selection.select("stdout"); let query = self.selection.select("stdout");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// Retrieves the user to be set for all commands. /// Retrieves the user to be set for all commands.
pub async fn user(&self) -> eyre::Result<String> { pub async fn user(&self) -> Result<String, DaggerError> {
let query = self.selection.select("user"); let query = self.selection.select("user");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -802,6 +920,9 @@ impl Container {
if let Some(include) = opts.include { if let Some(include) = opts.include {
query = query.arg("include", include); query = query.arg("include", include);
} }
if let Some(owner) = opts.owner {
query = query.arg("owner", owner);
}
return Container { return Container {
proc: self.proc.clone(), proc: self.proc.clone(),
@ -855,6 +976,8 @@ impl Container {
/// # Arguments /// # Arguments
/// ///
/// * `args` - Command to run instead of the container's default command (e.g., ["run", "main.go"]). /// * `args` - Command to run instead of the container's default command (e.g., ["run", "main.go"]).
///
/// If empty, the container's default command is used.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use /// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub fn with_exec(&self, args: Vec<impl Into<String>>) -> Container { pub fn with_exec(&self, args: Vec<impl Into<String>>) -> Container {
let mut query = self.selection.select("withExec"); let mut query = self.selection.select("withExec");
@ -876,6 +999,8 @@ impl Container {
/// # Arguments /// # Arguments
/// ///
/// * `args` - Command to run instead of the container's default command (e.g., ["run", "main.go"]). /// * `args` - Command to run instead of the container's default command (e.g., ["run", "main.go"]).
///
/// If empty, the container's default command is used.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use /// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub fn with_exec_opts<'a>( pub fn with_exec_opts<'a>(
&self, &self,
@ -888,6 +1013,9 @@ impl Container {
"args", "args",
args.into_iter().map(|i| i.into()).collect::<Vec<String>>(), args.into_iter().map(|i| i.into()).collect::<Vec<String>>(),
); );
if let Some(skip_entrypoint) = opts.skip_entrypoint {
query = query.arg("skipEntrypoint", skip_entrypoint);
}
if let Some(stdin) = opts.stdin { if let Some(stdin) = opts.stdin {
query = query.arg("stdin", stdin); query = query.arg("stdin", stdin);
} }
@ -1005,11 +1133,11 @@ impl Container {
/// * `path` - Location of the copied file (e.g., "/tmp/file.txt"). /// * `path` - Location of the copied file (e.g., "/tmp/file.txt").
/// * `source` - Identifier of the file to copy. /// * `source` - Identifier of the file to copy.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use /// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub fn with_file_opts( pub fn with_file_opts<'a>(
&self, &self,
path: impl Into<String>, path: impl Into<String>,
source: FileId, source: FileId,
opts: ContainerWithFileOpts, opts: ContainerWithFileOpts<'a>,
) -> Container { ) -> Container {
let mut query = self.selection.select("withFile"); let mut query = self.selection.select("withFile");
@ -1018,6 +1146,9 @@ impl Container {
if let Some(permissions) = opts.permissions { if let Some(permissions) = opts.permissions {
query = query.arg("permissions", permissions); query = query.arg("permissions", permissions);
} }
if let Some(owner) = opts.owner {
query = query.arg("owner", owner);
}
return Container { return Container {
proc: self.proc.clone(), proc: self.proc.clone(),
@ -1070,11 +1201,11 @@ impl Container {
/// * `path` - Location of the cache directory (e.g., "/cache/node_modules"). /// * `path` - Location of the cache directory (e.g., "/cache/node_modules").
/// * `cache` - Identifier of the cache volume to mount. /// * `cache` - Identifier of the cache volume to mount.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use /// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub fn with_mounted_cache_opts( pub fn with_mounted_cache_opts<'a>(
&self, &self,
path: impl Into<String>, path: impl Into<String>,
cache: CacheId, cache: CacheId,
opts: ContainerWithMountedCacheOpts, opts: ContainerWithMountedCacheOpts<'a>,
) -> Container { ) -> Container {
let mut query = self.selection.select("withMountedCache"); let mut query = self.selection.select("withMountedCache");
@ -1086,6 +1217,9 @@ impl Container {
if let Some(sharing) = opts.sharing { if let Some(sharing) = opts.sharing {
query = query.arg_enum("sharing", sharing); query = query.arg_enum("sharing", sharing);
} }
if let Some(owner) = opts.owner {
query = query.arg("owner", owner);
}
return Container { return Container {
proc: self.proc.clone(), proc: self.proc.clone(),
@ -1099,6 +1233,7 @@ impl Container {
/// ///
/// * `path` - Location of the mounted directory (e.g., "/mnt/directory"). /// * `path` - Location of the mounted directory (e.g., "/mnt/directory").
/// * `source` - Identifier of the mounted directory. /// * `source` - Identifier of the mounted directory.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub fn with_mounted_directory( pub fn with_mounted_directory(
&self, &self,
path: impl Into<String>, path: impl Into<String>,
@ -1115,12 +1250,41 @@ impl Container {
graphql_client: self.graphql_client.clone(), graphql_client: self.graphql_client.clone(),
}; };
} }
/// Retrieves this container plus a directory mounted at the given path.
///
/// # Arguments
///
/// * `path` - Location of the mounted directory (e.g., "/mnt/directory").
/// * `source` - Identifier of the mounted directory.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub fn with_mounted_directory_opts<'a>(
&self,
path: impl Into<String>,
source: DirectoryId,
opts: ContainerWithMountedDirectoryOpts<'a>,
) -> Container {
let mut query = self.selection.select("withMountedDirectory");
query = query.arg("path", path.into());
query = query.arg("source", source);
if let Some(owner) = opts.owner {
query = query.arg("owner", owner);
}
return Container {
proc: self.proc.clone(),
selection: query,
graphql_client: self.graphql_client.clone(),
};
}
/// Retrieves this container plus a file mounted at the given path. /// Retrieves this container plus a file mounted at the given path.
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `path` - Location of the mounted file (e.g., "/tmp/file.txt"). /// * `path` - Location of the mounted file (e.g., "/tmp/file.txt").
/// * `source` - Identifier of the mounted file. /// * `source` - Identifier of the mounted file.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub fn with_mounted_file(&self, path: impl Into<String>, source: FileId) -> Container { pub fn with_mounted_file(&self, path: impl Into<String>, source: FileId) -> Container {
let mut query = self.selection.select("withMountedFile"); let mut query = self.selection.select("withMountedFile");
@ -1133,12 +1297,41 @@ impl Container {
graphql_client: self.graphql_client.clone(), graphql_client: self.graphql_client.clone(),
}; };
} }
/// Retrieves this container plus a file mounted at the given path.
///
/// # Arguments
///
/// * `path` - Location of the mounted file (e.g., "/tmp/file.txt").
/// * `source` - Identifier of the mounted file.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub fn with_mounted_file_opts<'a>(
&self,
path: impl Into<String>,
source: FileId,
opts: ContainerWithMountedFileOpts<'a>,
) -> Container {
let mut query = self.selection.select("withMountedFile");
query = query.arg("path", path.into());
query = query.arg("source", source);
if let Some(owner) = opts.owner {
query = query.arg("owner", owner);
}
return Container {
proc: self.proc.clone(),
selection: query,
graphql_client: self.graphql_client.clone(),
};
}
/// Retrieves this container plus a secret mounted into a file at the given path. /// Retrieves this container plus a secret mounted into a file at the given path.
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `path` - Location of the secret file (e.g., "/tmp/secret.txt"). /// * `path` - Location of the secret file (e.g., "/tmp/secret.txt").
/// * `source` - Identifier of the secret to mount. /// * `source` - Identifier of the secret to mount.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub fn with_mounted_secret(&self, path: impl Into<String>, source: SecretId) -> Container { pub fn with_mounted_secret(&self, path: impl Into<String>, source: SecretId) -> Container {
let mut query = self.selection.select("withMountedSecret"); let mut query = self.selection.select("withMountedSecret");
@ -1151,6 +1344,34 @@ impl Container {
graphql_client: self.graphql_client.clone(), graphql_client: self.graphql_client.clone(),
}; };
} }
/// Retrieves this container plus a secret mounted into a file at the given path.
///
/// # Arguments
///
/// * `path` - Location of the secret file (e.g., "/tmp/secret.txt").
/// * `source` - Identifier of the secret to mount.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub fn with_mounted_secret_opts<'a>(
&self,
path: impl Into<String>,
source: SecretId,
opts: ContainerWithMountedSecretOpts<'a>,
) -> Container {
let mut query = self.selection.select("withMountedSecret");
query = query.arg("path", path.into());
query = query.arg("source", source);
if let Some(owner) = opts.owner {
query = query.arg("owner", owner);
}
return Container {
proc: self.proc.clone(),
selection: query,
graphql_client: self.graphql_client.clone(),
};
}
/// Retrieves this container plus a temporary directory mounted at the given path. /// Retrieves this container plus a temporary directory mounted at the given path.
/// ///
/// # Arguments /// # Arguments
@ -1205,6 +1426,9 @@ impl Container {
if let Some(permissions) = opts.permissions { if let Some(permissions) = opts.permissions {
query = query.arg("permissions", permissions); query = query.arg("permissions", permissions);
} }
if let Some(owner) = opts.owner {
query = query.arg("owner", owner);
}
return Container { return Container {
proc: self.proc.clone(), proc: self.proc.clone(),
@ -1268,7 +1492,9 @@ impl Container {
graphql_client: self.graphql_client.clone(), graphql_client: self.graphql_client.clone(),
}; };
} }
/// Establish a runtime dependency on a service. The service will be started automatically when needed and detached when it is no longer needed. /// Establish a runtime dependency on a service.
/// The service will be started automatically when needed and detached when it is
/// no longer needed, executing the default command if none is set.
/// The service will be reachable from the container via the provided hostname alias. /// The service will be reachable from the container via the provided hostname alias.
/// The service dependency will also convey to any files or directories produced by the container. /// The service dependency will also convey to any files or directories produced by the container.
/// Currently experimental; set _EXPERIMENTAL_DAGGER_SERVICES_DNS=0 to disable. /// Currently experimental; set _EXPERIMENTAL_DAGGER_SERVICES_DNS=0 to disable.
@ -1299,6 +1525,7 @@ impl Container {
/// ///
/// * `path` - Location of the forwarded Unix socket (e.g., "/tmp/socket"). /// * `path` - Location of the forwarded Unix socket (e.g., "/tmp/socket").
/// * `source` - Identifier of the socket to forward. /// * `source` - Identifier of the socket to forward.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub fn with_unix_socket(&self, path: impl Into<String>, source: SocketId) -> Container { pub fn with_unix_socket(&self, path: impl Into<String>, source: SocketId) -> Container {
let mut query = self.selection.select("withUnixSocket"); let mut query = self.selection.select("withUnixSocket");
@ -1311,6 +1538,34 @@ impl Container {
graphql_client: self.graphql_client.clone(), graphql_client: self.graphql_client.clone(),
}; };
} }
/// Retrieves this container plus a socket forwarded to the given Unix socket path.
///
/// # Arguments
///
/// * `path` - Location of the forwarded Unix socket (e.g., "/tmp/socket").
/// * `source` - Identifier of the socket to forward.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub fn with_unix_socket_opts<'a>(
&self,
path: impl Into<String>,
source: SocketId,
opts: ContainerWithUnixSocketOpts<'a>,
) -> Container {
let mut query = self.selection.select("withUnixSocket");
query = query.arg("path", path.into());
query = query.arg("source", source);
if let Some(owner) = opts.owner {
query = query.arg("owner", owner);
}
return Container {
proc: self.proc.clone(),
selection: query,
graphql_client: self.graphql_client.clone(),
};
}
/// Retrieves this container with a different command user. /// Retrieves this container with a different command user.
/// ///
/// # Arguments /// # Arguments
@ -1469,7 +1724,7 @@ impl Container {
}; };
} }
/// Retrieves the working directory for all commands. /// Retrieves the working directory for all commands.
pub async fn workdir(&self) -> eyre::Result<String> { pub async fn workdir(&self) -> Result<String, DaggerError> {
let query = self.selection.select("workdir"); let query = self.selection.select("workdir");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -1477,7 +1732,7 @@ impl Container {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Directory { pub struct Directory {
pub proc: Arc<Child>, pub proc: Option<Arc<Child>>,
pub selection: Selection, pub selection: Selection,
pub graphql_client: DynGraphQLClient, pub graphql_client: DynGraphQLClient,
} }
@ -1494,6 +1749,10 @@ pub struct DirectoryDockerBuildOpts<'a> {
/// The platform to build. /// The platform to build.
#[builder(setter(into, strip_option), default)] #[builder(setter(into, strip_option), default)]
pub platform: Option<Platform>, pub platform: Option<Platform>,
/// Secrets to pass to the build.
/// They will be mounted at /run/secrets/[secret-name].
#[builder(setter(into, strip_option), default)]
pub secrets: Option<Vec<SecretId>>,
/// Target build stage to build. /// Target build stage to build.
#[builder(setter(into, strip_option), default)] #[builder(setter(into, strip_option), default)]
pub target: Option<&'a str>, pub target: Option<&'a str>,
@ -1612,6 +1871,9 @@ impl Directory {
if let Some(target) = opts.target { if let Some(target) = opts.target {
query = query.arg("target", target); query = query.arg("target", target);
} }
if let Some(secrets) = opts.secrets {
query = query.arg("secrets", secrets);
}
return Container { return Container {
proc: self.proc.clone(), proc: self.proc.clone(),
@ -1624,7 +1886,7 @@ impl Directory {
/// # Arguments /// # Arguments
/// ///
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use /// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub async fn entries(&self) -> eyre::Result<Vec<String>> { pub async fn entries(&self) -> Result<Vec<String>, DaggerError> {
let query = self.selection.select("entries"); let query = self.selection.select("entries");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -1638,7 +1900,7 @@ impl Directory {
pub async fn entries_opts<'a>( pub async fn entries_opts<'a>(
&self, &self,
opts: DirectoryEntriesOpts<'a>, opts: DirectoryEntriesOpts<'a>,
) -> eyre::Result<Vec<String>> { ) -> Result<Vec<String>, DaggerError> {
let mut query = self.selection.select("entries"); let mut query = self.selection.select("entries");
if let Some(path) = opts.path { if let Some(path) = opts.path {
@ -1652,7 +1914,7 @@ impl Directory {
/// # Arguments /// # Arguments
/// ///
/// * `path` - Location of the copied directory (e.g., "logs/"). /// * `path` - Location of the copied directory (e.g., "logs/").
pub async fn export(&self, path: impl Into<String>) -> eyre::Result<bool> { pub async fn export(&self, path: impl Into<String>) -> Result<bool, DaggerError> {
let mut query = self.selection.select("export"); let mut query = self.selection.select("export");
query = query.arg("path", path.into()); query = query.arg("path", path.into());
@ -1676,7 +1938,7 @@ impl Directory {
}; };
} }
/// The content-addressed identifier of the directory. /// The content-addressed identifier of the directory.
pub async fn id(&self) -> eyre::Result<DirectoryId> { pub async fn id(&self) -> Result<DirectoryId, DaggerError> {
let query = self.selection.select("id"); let query = self.selection.select("id");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -1977,20 +2239,20 @@ impl Directory {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct EnvVariable { pub struct EnvVariable {
pub proc: Arc<Child>, pub proc: Option<Arc<Child>>,
pub selection: Selection, pub selection: Selection,
pub graphql_client: DynGraphQLClient, pub graphql_client: DynGraphQLClient,
} }
impl EnvVariable { impl EnvVariable {
/// The environment variable name. /// The environment variable name.
pub async fn name(&self) -> eyre::Result<String> { pub async fn name(&self) -> Result<String, DaggerError> {
let query = self.selection.select("name"); let query = self.selection.select("name");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// The environment variable value. /// The environment variable value.
pub async fn value(&self) -> eyre::Result<String> { pub async fn value(&self) -> Result<String, DaggerError> {
let query = self.selection.select("value"); let query = self.selection.select("value");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -1998,14 +2260,14 @@ impl EnvVariable {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct File { pub struct File {
pub proc: Arc<Child>, pub proc: Option<Arc<Child>>,
pub selection: Selection, pub selection: Selection,
pub graphql_client: DynGraphQLClient, pub graphql_client: DynGraphQLClient,
} }
impl File { impl File {
/// Retrieves the contents of the file. /// Retrieves the contents of the file.
pub async fn contents(&self) -> eyre::Result<String> { pub async fn contents(&self) -> Result<String, DaggerError> {
let query = self.selection.select("contents"); let query = self.selection.select("contents");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2015,7 +2277,7 @@ impl File {
/// # Arguments /// # Arguments
/// ///
/// * `path` - Location of the written directory (e.g., "output.txt"). /// * `path` - Location of the written directory (e.g., "output.txt").
pub async fn export(&self, path: impl Into<String>) -> eyre::Result<bool> { pub async fn export(&self, path: impl Into<String>) -> Result<bool, DaggerError> {
let mut query = self.selection.select("export"); let mut query = self.selection.select("export");
query = query.arg("path", path.into()); query = query.arg("path", path.into());
@ -2023,7 +2285,7 @@ impl File {
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// Retrieves the content-addressed identifier of the file. /// Retrieves the content-addressed identifier of the file.
pub async fn id(&self) -> eyre::Result<FileId> { pub async fn id(&self) -> Result<FileId, DaggerError> {
let query = self.selection.select("id"); let query = self.selection.select("id");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2039,7 +2301,7 @@ impl File {
}; };
} }
/// Gets the size of the file, in bytes. /// Gets the size of the file, in bytes.
pub async fn size(&self) -> eyre::Result<isize> { pub async fn size(&self) -> Result<isize, DaggerError> {
let query = self.selection.select("size"); let query = self.selection.select("size");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2065,7 +2327,7 @@ impl File {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct GitRef { pub struct GitRef {
pub proc: Arc<Child>, pub proc: Option<Arc<Child>>,
pub selection: Selection, pub selection: Selection,
pub graphql_client: DynGraphQLClient, pub graphql_client: DynGraphQLClient,
} }
@ -2080,7 +2342,7 @@ pub struct GitRefTreeOpts<'a> {
impl GitRef { impl GitRef {
/// The digest of the current value of this ref. /// The digest of the current value of this ref.
pub async fn digest(&self) -> eyre::Result<String> { pub async fn digest(&self) -> Result<String, DaggerError> {
let query = self.selection.select("digest"); let query = self.selection.select("digest");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2124,7 +2386,7 @@ impl GitRef {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct GitRepository { pub struct GitRepository {
pub proc: Arc<Child>, pub proc: Option<Arc<Child>>,
pub selection: Selection, pub selection: Selection,
pub graphql_client: DynGraphQLClient, pub graphql_client: DynGraphQLClient,
} }
@ -2147,7 +2409,7 @@ impl GitRepository {
}; };
} }
/// Lists of branches on the repository. /// Lists of branches on the repository.
pub async fn branches(&self) -> eyre::Result<Vec<String>> { pub async fn branches(&self) -> Result<Vec<String>, DaggerError> {
let query = self.selection.select("branches"); let query = self.selection.select("branches");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2185,7 +2447,7 @@ impl GitRepository {
}; };
} }
/// Lists of tags on the repository. /// Lists of tags on the repository.
pub async fn tags(&self) -> eyre::Result<Vec<String>> { pub async fn tags(&self) -> Result<Vec<String>, DaggerError> {
let query = self.selection.select("tags"); let query = self.selection.select("tags");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2193,7 +2455,7 @@ impl GitRepository {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Host { pub struct Host {
pub proc: Arc<Child>, pub proc: Option<Arc<Child>>,
pub selection: Selection, pub selection: Selection,
pub graphql_client: DynGraphQLClient, pub graphql_client: DynGraphQLClient,
} }
@ -2334,7 +2596,7 @@ impl Host {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct HostVariable { pub struct HostVariable {
pub proc: Arc<Child>, pub proc: Option<Arc<Child>>,
pub selection: Selection, pub selection: Selection,
pub graphql_client: DynGraphQLClient, pub graphql_client: DynGraphQLClient,
} }
@ -2351,7 +2613,7 @@ impl HostVariable {
}; };
} }
/// The value of this variable. /// The value of this variable.
pub async fn value(&self) -> eyre::Result<String> { pub async fn value(&self) -> Result<String, DaggerError> {
let query = self.selection.select("value"); let query = self.selection.select("value");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2359,20 +2621,20 @@ impl HostVariable {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Label { pub struct Label {
pub proc: Arc<Child>, pub proc: Option<Arc<Child>>,
pub selection: Selection, pub selection: Selection,
pub graphql_client: DynGraphQLClient, pub graphql_client: DynGraphQLClient,
} }
impl Label { impl Label {
/// The label name. /// The label name.
pub async fn name(&self) -> eyre::Result<String> { pub async fn name(&self) -> Result<String, DaggerError> {
let query = self.selection.select("name"); let query = self.selection.select("name");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// The label value. /// The label value.
pub async fn value(&self) -> eyre::Result<String> { pub async fn value(&self) -> Result<String, DaggerError> {
let query = self.selection.select("value"); let query = self.selection.select("value");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2380,26 +2642,26 @@ impl Label {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Port { pub struct Port {
pub proc: Arc<Child>, pub proc: Option<Arc<Child>>,
pub selection: Selection, pub selection: Selection,
pub graphql_client: DynGraphQLClient, pub graphql_client: DynGraphQLClient,
} }
impl Port { impl Port {
/// The port description. /// The port description.
pub async fn description(&self) -> eyre::Result<String> { pub async fn description(&self) -> Result<String, DaggerError> {
let query = self.selection.select("description"); let query = self.selection.select("description");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// The port number. /// The port number.
pub async fn port(&self) -> eyre::Result<isize> { pub async fn port(&self) -> Result<isize, DaggerError> {
let query = self.selection.select("port"); let query = self.selection.select("port");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// The transport layer network protocol. /// The transport layer network protocol.
pub async fn protocol(&self) -> eyre::Result<NetworkProtocol> { pub async fn protocol(&self) -> Result<NetworkProtocol, DaggerError> {
let query = self.selection.select("protocol"); let query = self.selection.select("protocol");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2407,7 +2669,7 @@ impl Port {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Project { pub struct Project {
pub proc: Arc<Child>, pub proc: Option<Arc<Child>>,
pub selection: Selection, pub selection: Selection,
pub graphql_client: DynGraphQLClient, pub graphql_client: DynGraphQLClient,
} }
@ -2434,25 +2696,25 @@ impl Project {
}; };
} }
/// install the project's schema /// install the project's schema
pub async fn install(&self) -> eyre::Result<bool> { pub async fn install(&self) -> Result<bool, DaggerError> {
let query = self.selection.select("install"); let query = self.selection.select("install");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// name of the project /// name of the project
pub async fn name(&self) -> eyre::Result<String> { pub async fn name(&self) -> Result<String, DaggerError> {
let query = self.selection.select("name"); let query = self.selection.select("name");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// schema provided by the project /// schema provided by the project
pub async fn schema(&self) -> eyre::Result<String> { pub async fn schema(&self) -> Result<String, DaggerError> {
let query = self.selection.select("schema"); let query = self.selection.select("schema");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// sdk used to generate code for and/or execute this project /// sdk used to generate code for and/or execute this project
pub async fn sdk(&self) -> eyre::Result<String> { pub async fn sdk(&self) -> Result<String, DaggerError> {
let query = self.selection.select("sdk"); let query = self.selection.select("sdk");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2460,7 +2722,7 @@ impl Project {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Query { pub struct Query {
pub proc: Arc<Child>, pub proc: Option<Arc<Child>>,
pub selection: Selection, pub selection: Selection,
pub graphql_client: DynGraphQLClient, pub graphql_client: DynGraphQLClient,
} }
@ -2567,7 +2829,7 @@ impl Query {
}; };
} }
/// The default platform of the builder. /// The default platform of the builder.
pub async fn default_platform(&self) -> eyre::Result<Platform> { pub async fn default_platform(&self) -> Result<Platform, DaggerError> {
let query = self.selection.select("defaultPlatform"); let query = self.selection.select("defaultPlatform");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2829,20 +3091,20 @@ impl Query {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Secret { pub struct Secret {
pub proc: Arc<Child>, pub proc: Option<Arc<Child>>,
pub selection: Selection, pub selection: Selection,
pub graphql_client: DynGraphQLClient, pub graphql_client: DynGraphQLClient,
} }
impl Secret { impl Secret {
/// The identifier for this secret. /// The identifier for this secret.
pub async fn id(&self) -> eyre::Result<SecretId> { pub async fn id(&self) -> Result<SecretId, DaggerError> {
let query = self.selection.select("id"); let query = self.selection.select("id");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// The value of this secret. /// The value of this secret.
pub async fn plaintext(&self) -> eyre::Result<String> { pub async fn plaintext(&self) -> Result<String, DaggerError> {
let query = self.selection.select("plaintext"); let query = self.selection.select("plaintext");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2850,14 +3112,14 @@ impl Secret {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Socket { pub struct Socket {
pub proc: Arc<Child>, pub proc: Option<Arc<Child>>,
pub selection: Selection, pub selection: Selection,
pub graphql_client: DynGraphQLClient, pub graphql_client: DynGraphQLClient,
} }
impl Socket { impl Socket {
/// The content-addressed identifier of the socket. /// The content-addressed identifier of the socket.
pub async fn id(&self) -> eyre::Result<SocketId> { pub async fn id(&self) -> Result<SocketId, DaggerError> {
let query = self.selection.select("id"); let query = self.selection.select("id");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await

View File

@ -1,6 +1,7 @@
#![deny(warnings)] #![deny(warnings)]
mod client; mod client;
pub mod errors;
mod gen; mod gen;
pub mod logging; pub mod logging;
mod querybuilder; mod querybuilder;

View File

@ -1,9 +1,10 @@
use std::{collections::HashMap, ops::Add, sync::Arc}; use std::{collections::HashMap, ops::Add, sync::Arc};
use dagger_core::graphql_client::DynGraphQLClient; use dagger_core::graphql_client::DynGraphQLClient;
use eyre::Context;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::errors::{DaggerError, DaggerUnpackError};
pub fn query() -> Selection { pub fn query() -> Selection {
Selection::default() Selection::default()
} }
@ -92,7 +93,7 @@ impl Selection {
s s
} }
pub fn build(&self) -> eyre::Result<String> { pub fn build(&self) -> Result<String, DaggerError> {
let mut fields = vec!["query".to_string()]; let mut fields = vec!["query".to_string()];
for sel in self.path() { for sel in self.path() {
@ -117,7 +118,7 @@ impl Selection {
Ok(fields.join("{") + &"}".repeat(fields.len() - 1)) Ok(fields.join("{") + &"}".repeat(fields.len() - 1))
} }
pub async fn execute<D>(&self, gql_client: DynGraphQLClient) -> eyre::Result<D> pub async fn execute<D>(&self, gql_client: DynGraphQLClient) -> Result<D, DaggerError>
where where
D: for<'de> Deserialize<'de>, D: for<'de> Deserialize<'de>,
{ {
@ -127,7 +128,7 @@ impl Selection {
let resp: Option<serde_json::Value> = match gql_client.query(&query).await { let resp: Option<serde_json::Value> = match gql_client.query(&query).await {
Ok(r) => r, Ok(r) => r,
Err(e) => eyre::bail!(e), Err(e) => return Err(DaggerError::Query(e)),
}; };
let resp: Option<D> = self.unpack_resp(resp)?; let resp: Option<D> = self.unpack_resp(resp)?;
@ -151,7 +152,10 @@ impl Selection {
selections selections
} }
pub(crate) fn unpack_resp<D>(&self, resp: Option<serde_json::Value>) -> eyre::Result<Option<D>> pub(crate) fn unpack_resp<D>(
&self,
resp: Option<serde_json::Value>,
) -> Result<Option<D>, DaggerError>
where where
D: for<'de> Deserialize<'de>, D: for<'de> Deserialize<'de>,
{ {
@ -161,21 +165,23 @@ impl Selection {
} }
} }
fn unpack_resp_value<D>(&self, r: serde_json::Value) -> eyre::Result<D> fn unpack_resp_value<D>(&self, r: serde_json::Value) -> Result<D, DaggerError>
where where
D: for<'de> Deserialize<'de>, D: for<'de> Deserialize<'de>,
{ {
if let Some(o) = r.as_object() { if let Some(o) = r.as_object() {
let keys = o.keys(); let keys = o.keys();
if keys.len() != 1 { if keys.len() != 1 {
eyre::bail!("too many nested objects inside graphql response") return Err(DaggerError::Unpack(DaggerUnpackError::TooManyNestedObjects));
} }
let first = keys.into_iter().next().unwrap(); let first = keys.into_iter().next().unwrap();
return self.unpack_resp_value(o.get(first).unwrap().clone()); return self.unpack_resp_value(o.get(first).unwrap().clone());
} }
serde_json::from_value::<D>(r).context("could not deserialize response") serde_json::from_value::<D>(r)
.map_err(DaggerUnpackError::Deserialize)
.map_err(DaggerError::Unpack)
} }
} }

View File

@ -111,9 +111,9 @@ async fn test_err_message() {
assert_eq!(alpine.is_err(), true); assert_eq!(alpine.is_err(), true);
let err = alpine.expect_err("Tests expect err"); let err = alpine.expect_err("Tests expect err");
let error_msg = r#" let error_msg = r#"failed to query dagger engine: domain error:
GQLClient Error: Look at json field for more details Look at json field for more details
Message: pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
"#; "#;
assert_eq!(err.to_string().as_str(), error_msg); assert_eq!(err.to_string().as_str(), error_msg);