#@wompfox can I ask you some weird
1 messages ยท Page 1 of 1 (latest)
I remember that we chatted in person about SDK generation
can you point me to who in the Dagger team is doing some of that magic?
SDK generation as "creating a new SDK"?
several people in the Dagger team have different context about that. Given your TZ Mauricio, I'd say @cloud pewter and @brisk yoke might be the best Dagger people to ping.
Hey, you can ask your questions here, I'm online ๐
@formal juniper Feel free to complete if I misses any details since you built an SDK last month ๐
So a SDK is a module that is able to load user's code that will extends Dagger capabilities and be callable from the CLI.
All modules created with Dagger uses a SDK, for example you can use the Go SDK to write a module using the Go programming language (see: https://docs.dagger.io/api/custom-functions for more details)
It's composed of multiple things:
- A client: Used by the end user to use the Dagger API in his original language. This client is generated from a GraphQL schema that defines all the type definitions (typedef) exposed by Dagger and its differents extenstions.
- A introspector: This will parse the user code and transform it into compatible type definition and then register it to the Dagger API, that's how we dynamically extends the GraphQL schema ๐
For example in Go:
type Example struct {}
func (e Example) Hello(ctx context.Context, myArg string) string { ... }
This will register a typedef on Dagger of type Object, named Example, that has a function Hello that returns a STRING and accept an argument named myArg of type STRING.
It's quite standard, basically you convert user code into readable typedef, all of this is done dynamically with API call that you need to make ๐
- A runtime: This will create a container to run the users module and call the given function
dagger call hello --my-arg foowill execute the functionHellofor example.
All theses features are exposed with a Dagger module that needs to implements 2 functions:
ModuleRuntime: Create the runtime and call the user's module, either to register typedefinition or execute a function.Codegen: Generate the client and any necessary files needed to execute the module (some statically generate the user's code execution like Go, other does it dynamically like Typescript but copy the library locally, you're free on that side)
I think that's pretty much what you need to create a SDK, the best way to learn is to look at how other SDK are made (Typescript & Python) are the easiest to understand in my opinion ๐
hmm.. not sure this is what I was looking for..
@formal juniper so all the Dagger SDKs are handcrafted today?
I thought at least some bits of the SDKs were automatically generated based on graphql
yes, a lot of the SDK is generated
That's the solution to also generate proper code based on your dependencies.
If you module needs to access an other module, the code generation will include types to this module
As en example, I pushed a fresh module with the generated sources, so you can have an idea of what's generated vs what's not: https://github.com/eunomie/dagger-java-example
- the user module code: https://github.com/eunomie/dagger-java-example/blob/main/src/main/java/io/dagger/modules/daggerjavaexample/DaggerJavaExample.java
- in this directory you can find the code that is written by hand: https://github.com/eunomie/dagger-java-example/tree/main/target/generated-sources/dagger-io/io/dagger
- ๐ค this is the generated entrypoint of the module, use to register the different functions, fields, objects to Dagger and to dispatch the calls: https://github.com/eunomie/dagger-java-example/blob/main/target/generated-sources/entrypoint/io/dagger/gen/entrypoint/Entrypoint.java
- ๐ค all the files here are generated based on the types provided by the dagger engine: https://github.com/eunomie/dagger-java-example/tree/main/target/generated-sources/dagger-module/io/dagger/client
It's java, and it's not the SDK code itself, but that can help to see the differences between generated vs non generated code
As an other example, here is the code responsible to generate the TypeScript types: https://github.com/dagger/dagger/tree/main/cmd/codegen/generator/typescript
In this case, the generator is in Go. That's also the solution I used while playing with a ruby SDK.
So basically I'd say there's three parts in what we can call a SDK:
- the runtime: this is a specific module in which everything will happen, both code generation and execution. You can see that in some way as "the container in which the code will run".
- I don't know how strong the constraint is, but it's probably better to have this runtime in Go as it's a special SDK
- here is the code for the runtime module of the Java SDK: https://github.com/dagger/dagger/blob/main/sdk/java/runtime/main.go I tried to add comments as a start to a SDK documentation
- the codegen tool: it can be in any language, as we saw it's in Go for typescript, but it's in Java for Java SDK https://github.com/dagger/dagger/tree/main/sdk/java/dagger-codegen-maven-plugin
- this tool will be called by the
codegenfunction of the runtime above - this tool will generate all the dynamic types based on the list of types returned by the Dagger engine (types will be in a "introspection json" file) (the output depends on the languages)
- this tool will be called by the
- the language specific SDK code (I don't have a nice name, as SDK conflicts, in some way that's also runtime but it conflicts): basically the code written by hand to be the glue between the Dagger platform and the generated types + user code
- this is the very specific part to any SDK. A very short description could be this is a simple GraphQL client, but it's a bit more, there's code about serialization (that is a bit more complex than what we can think at first look)
- here is the one from java https://github.com/dagger/dagger/tree/main/sdk/java/dagger-java-sdk (this matches roughly the
target/generated-sources/dagger-ioin the message above)
In some way I'd say there's one more part, but it really depends on the language: the entrypoint.
The entrypoint is the code that will be called by the ModuleRuntime part of the SDK runtime. In multiple SDKs this is a dynamic code, so written only once, that will find the objects to register and call them.
In Java this code is also generated during the packaging. It's an extra phase, but as said, it depends on the languages and choices. In Java this is done by this code https://github.com/dagger/dagger/tree/main/sdk/java/dagger-java-annotation-processor
I don't know if I introduces more complexity or if that helps to understand better ๐
I have the idea to create a full doc of building a new SDK, but it still not done. Maybe I'll convert all that, that could be great
yes.. I would love to help you review that doc.. that will help me to understand the details
which language would you like to write an SDK for?