#daggerizing an npm application
1 messages · Page 1 of 1 (latest)
Normally I'd start by creating the required environment in dagger and running the existing npm scripts there. The quickstart is a pretty good reference on how to set this up https://docs.dagger.io/quickstart/test
i think what's tripping me up is that this still has you pass in --source=. from the cli
whereas I just want to define that directly in the dagger module
since it's static
Got it, it sounds like you might want 'context directories' described here https://docs.dagger.io/api/arguments#directories-and-files
i dont see how that helps?
@func()
async readDir(
@argument({ defaultPath: "/" }) source: Directory,
): Promise<string[]> {
return await source.entries()
}
still takes a Directory as an argument
Yeah so this would make source an optional argument, defaulting to the root of the repo. So in the quickstart command it would just be dagger call test
Unless I'm misunderstanding the question 🙂
hmm maybe vscode is lying to me about the error
it's saying "loadSource expected 1 argument but got 0"
Ah yeah right now if you're calling the function from another function within the same module, the argument would still be required because the defaulting happens at the dagger layer between modules or in the CLI
A common pattern is to have the project source as a field on the module's class rather than an argument to each function since every function needs the source anyway. In that case you can still default the source to your repo in the class' constructor
ah, that makes sense
and if i wanted to package this up in the future and turn it into a shared module, then source would just be defined at the module level and it'd be transparent to the caller?
Yeah constructor args can always be passed/overridden from callers, so it still works in that case!
i still feel like i'm missing something .. i'm basically trying to package up some shared scripts and make them reusable across a couple of projects. Is a dagger module what I want? Do I actually really want to be producing a container which has the scripts I'm trying to share, and then consumers reference that container?
https://github.com/camptocamp/daggerverse/blob/d13fd72436571a2ed6a071dc9e807a53668206ff/sass/main.go this seems like a good example of wrapping a local file up into a module to reuse
Yeah that's a good example. Providing a container is usually ideal for consumers of the modules because any dependencies for the script would then be bundled with it, so the scripts are 100% portable. The overlay option from the module you linked is a cool solution too
It seems like a major gap that this is somewhat complicated though. I feel like I shouldn’t have to figure out a clever way to bundle a script and make it reusable- having to publish/share an image vs share a dagger module feels like it’s a busted abstraction
Totally! What do you picture as the ideal solution? Ignoring current limitations
I’m not 100% of the API but at a high level, “here’s a relative path within the dagger directory- copy them into an image at this path”
container.WithDirectory would make the most sense to me (and I don’t see a good reason for a new API surface for this). I guess the new API would be to “make this local path a Directory”, something like ,Directory.fromLocal(relativePath: str): Directory
so if I have a repo or dagger module with a directory scripts/ that has a bunch of bash scripts I want included, something like this works today
@func()
async test(
@argument({ defaultPath: "/scripts" }) scripts: Directory,
): Promise<string> {
return await dag
.container()
.from("alpine:latest")
.withMountedDirectory("/foo", scripts)
.withExec(["sh", "-c", "/foo/myscript.sh"])
.stdout();
}
and I can just run dagger call test without passing --scripts and it'll get the directory at scripts/ in my repo. Is that what you mean or specifically grabbing this local path in code at an arbitrary time? I can show another example of that if its what you're going for
@copper canyon but i guess what I'm trying to do is provide a module/have it be called as API
like i basically want to distribute a custom CLI took that is used by various teams and projects
ah maybe i'm missing a key part of
Ah yeah right now if you're calling the function from another function within the same module, the argument would still be required because the defaulting happens at the dagger layer between modules or in the CLI
..
you said
from another function within the same module
does that mean if i defined a completely separate module, using code like you showed above, then the caller would not need to pass inscripts? I really don't even want it to be possible to pass in alternate scripts though
I really don't even want it to be possible to pass in alternate scripts though
This is doable too. What kind of things might someone pass to your function? I'll write up an example
We use contentful as a CMS, so we have some scripts that pull data from contentful, and then reformat it before writing it out to disk, and then our npm build generates a static site. We pass in some API call data (environment id, token information)
for this example I run this script:
#!/bin/sh
mkdir /out
echo "HELLO WORLD" > /out/hello.txt
in this function:
@func()
test(token: Secret): Directory {
return dag
.container()
.from("alpine:latest")
.withSecretVariable("TOKEN", token)
.withMountedDirectory(
"/foo",
dag.currentModule().source().directory("scripts"),
)
.withExec(["sh", "-c", "/foo/myscript.sh"])
.directory("/out");
}
output:
dagger call test --token env:TOKEN file --path hello.txt contents
âś” connect 0.4s
âś” loading module 0.8s
âś” parsing command line arguments 0.0s
âś” setSecret(name: "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"): Secret! 0.0s
âś” example: Example! 0.0s
âś” Example.test(
token: âś” setSecret(name: "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"): Secret! 0.0s
): Directory! 3.2s
âś” Directory.file(path: "hello.txt"): File! 0.0s
âś” File.contents: String! 0.0s
Full trace at https://dagger.cloud/kpenfound/traces/db655024d35db4fb01ca2485e69cd939
HELLO WORLD
and then if I wanted to use that from another module it would be something like.
@func
build(source: Directory): Container {
return this.buildEnv()
.withDirectory("/content-dir", Contentful.test(secret))
}
yes exactly!
damn, thank you!
no problem, hopefully that feels like a better solution than what we discussed earlier 🙂
yeah! this feels like what i was looking for initially
dag.currentModule().source().directory("scripts"),
seems to be the key thing I couldn't find right away in the docs
one thing i've seen in some projects recently that i've really liked (https://docs.dagster.io/getting-started), for example is an LLM chat bot that can help navigate the APIs
the dagster one is nice, it's got github in its context and will point you to GH issues/discussions as well as docs
Yeah that's good feedback! We've tried some solutions like that in the past with varying success. But navigating our split knowledge base across docs/github/discord is a serious challenge. I like that dagster example, thanks for linking!
i swear i saw a project somewhere that was trying to solve that exact problem
because .. yeah, it's tough
i really appreciate the help here though 🙏
There's a few out there for sure. The real trick is that (IMO) having an LLM that gives incorrect information is worse than having nothing
is it something how you create dagger.Directory object? I am loosing my mind right now
can somebody help me how to make dagger.Directory from str?