#Blueprints ๐ฆ + Toolchains ๐ ๏ธ
1 messages ยท Page 1 of 1 (latest)
Hey @azure bronze just been messing around with blueprints and toolchains. A kind of cool composability use case has come up and I wanted to share it with you.
The context is we want to install a blueprint module in repos which will serve as an "orchestrator", running various tasks
Those tasks would include, building the code, running the linter, pushing an image to the registry etc.
We plan on loosely pinning this module to our repos so we can roll out updates to them without having to continually update each one.
However this last point introduces a caveat which is that now we cannot guarantee a build will work the same twice...
So naturally we were leaning towards somehow hard pinning tasks that build the source and run the linter, so that they had the same behaviour each time...
I can see this working with toolchains.
{
"name": "toolchains-test",
"engineVersion": "v0.19.6",
"blueprint": {
"name": "go-service",
"source": "../daggerflows/go-service"
},
"toolchains": [
{
"name": "go-lint",
"source": "../modules/go-lint"
}
]
}
Please ignore the relative references... imagine the first one (blueprint) was loosely pinned to main and the second one was hard pinned to some specific tag.
I really don't think you want any form of loose pinning at runtime anywhere in dagger
In this setup developers can run the linter directly via toolchains - very handy:
dagger call go-lint check
Well this is where the sort of benefits of both might work for us...
(I understand why it's tempting to want it)
I discovered that the blueprint module can actually see the linter as a dependency module.
Using a bit of graphql I can make the blueprint go-service call the linter module:
func (g *GoService) linter() *querybuilder.Selection {
client := querybuilder.Query().Client(dag.GraphQLClient())
return client.Select("goLint").
Arg("linterVersion", "0.173.0").
Arg("goVersion", "1.24.3").
Arg("ssh", g.SSH)
}
func (g *GoService) Build(ctx context.Context) error {
return g.linter().
Select("check").
Arg("since", "asdf").
Arg("src", g.Src).
Execute(ctx)
}
Can you describe your criteria for a valid use of loose pinning? What has to be true for it to be worth losing repeatability?
There are certain things we want to be able to distribute to our developers without them really knowing or caring. For example security scanning, integration with pager duty, integrations with DX
Btw we're in the process of adopting toolchains in a big way on our own CI -> https://github.com/dagger/dagger/issues/11398
14 toolchains and counting ๐ https://github.com/shykes/dagger/tree/ci-faster-load/toolchains
Yeah this is great.
One of the advantages of toolchains is that you don't need to proxy access to dependencies which can actually perform some task for the end user.
Exactly. That proxy boilerplate really adds up..
What is dang ?
It's a dagger-native scripting language that requires no codegen, and as a result is ultra-fast. Part of our "need for speed" roadmap.
Note: the blueprint-for-vendoring pattern itself is not specific to dang ๐
Oh very nice.
Toolchains should help us out a lot. We want to install things like the linter and the compiler as seperate toolchains in repos.
And I found out that the blueprint module can also see toolchain modules and can invoke them over the graphql api...
Which suits our hacky not-recommended loose pinning strategy
hahaha
Perhaps we need to have more of a chat about that so you are aware of what we're doing.
I can't promise that this will keep working
Yes I would love that!
Yeah thats understandable and is the reason I wanted to share with you in the first place
I felt I was going a bit offroad, and we want to be supported no matter what so yeah
If you have some time it would be good to have a meeting with my team at some point
And I want to make sure our design takes your actual use case into account
Don't want to take any toys away unless it's to trade them for better toys ๐
Sounds good
@muted comet what's your timeline on this? The sooner the better?
By "this" I mean your overall dagger project. Are you under time pressure for certain milestones, or more of a continuous improvement?
Can do next week
Right now the rollout is only to 3 teams in the company.
We have collected a lot of feedback and now are trying to "finalise" the design of the CI.
Next year will be crunch time.
So ideally we would like to have the kinks out by the end of the year
OK so in addition to learning about the details of your rollout, we should also show you the details of what we're shipping next ๐ So you can take that into account (nothing breaking don't worry, except maybe that graphql backdoor ๐ )
(sent you a DM)
This is an interesting use case that we will also have. We distribute CI code to our developers all the time without hard pinning so we can actually perform necessary updates/upgrades any time we need to. The devs can of course hard pin if they want to, but they are on their own at that point.
Essentially this is how our jenkins shared libraries work. The default most folks use is a soft pin. I'm not saying that's right for dagger. But we would probably still have some toolchains that we want to distribute with that model. Mostly liniting and security related.
What about the combination of hard-pinning and auto-pushing an explicit update on git? That way you get the best of both worlds
It's not like you have to open a PR and wait for the dev (who doesn't care) to approve it. You can just direct-push to git. It's just as intrusive, gives you just as much control, but you get a proper audit trail (and repeatability)
But, I guess you could also get the audit trail from the trace if we allowed soft-pinning
like force push an update to the developer's main? That would probably be a very difficult task to achive given enterprise access guardrails
Or, what about allowing the client to auto-update the pin on run? So they would get an uncommitted diff when they run if it happens to update the pin
people are very ansy about central teams touching their repos
I get it. It's kind of an illusion since you're still "touching" how their repo runs ๐
Oh I get it.. Tell that to the Enterprise security teams ๐
What about the dependabot pattern?
You probably have a system in place, dev-tolerated and security-approved, for every other central dependency getting pushed to devs?
This may work. But still requires some kind of central control like a Github App and read access to repos. Without going into details, I can tell you that it will be an uphil battle.
For CI libraries, yes
basically it's the soft pin
Let's say we enable soft-pinning (perhaps as an opt-in flag). Aren't you worried about the downsides? Ie. lack of end-to-end repeatability
Is there a scenario that leads to "well Dagger isn't reliable" because of soft pinning
I am 100%. But even with pinning dagger, there can be repeatability issues right? Eg: we don't pin all the containers we pull to a SHA.
There is no reliable way to use something like renovate for dagger (yet) so pinning all docker images we pull to a SHA is in of itself a security issue.
Yeah that's true. (Although I do have a POC for a dagger.lock that would catch everything ๐
sadly, with what we have today, that's a compromise everyone is ok with. But with dagger I'd love to change that mindset. However, we'll need something like dependabot/renovate. We use renovate today, so if there's a renovate plugin we can use that
Actually a lockfile could make it more ok to have softpinning I suppose
That's definitely the right step. If dagger can work with something like renovate, we can utilize existing tooling to open PRs for folks
What's missing on our end to work with renovate?
I guess develop a renovate plugin?
potentially a combination of a datasource (https://docs.renovatebot.com/modules/datasource/) and a manager (https://docs.renovatebot.com/modules/manager/)
Renovate documentation.
Renovate documentation.
Suddenly soft pin seems so much more appealing ๐
Someone started down the renovate path a long time ago https://discord.com/channels/707636530424053791/1370547151180009753. It was tedious and not official
I think a comprehensive lockfile makes that more appealing. That's essentially what we do with other package managers.
maybe combined with a way for you to centralize management of lockfiles on behalf of your devs? to bypass the renovate dance
so it would be perfectly repeatable but on your terms
and btw the lockfile also captures runtime dependencies like container pulls from third parties etc
so it makes not just the build of your dagger module repeatable, but it's execution also
That makes sense!
maybe? just the tracing alone is a big add. Makes it easy to troubleshoot stuff.