#+up services for modules
1 messages ยท Page 1 of 1 (latest)
Was mostly just trying to solve the composition problem for reusable modules that typically need services like playwright going off of what we have now.
@cosmic bear and I talked through it and came up with a better plan that doesn't directly need to get a Service from an UpGroup. Something like:
[modules.docusaurus]
source = "github.com/example/docusaurus@v1.0"
[modules.playwright]
source = "github.com/example/playwright@v1.0"
config.app = { from = "docusaurus:serve" }
It gets slightly more comlicated with Collections where the docusarus module could provide a +up for every site that exists in the Workspace, but as long as we can key on it this kind of approach should work.
Yeah to summarize where my head is at:
- Each playwright config needs a single target service to connect to. This is defined in the test framework itself.
- The "test containers pattern" does NOT make sense for playwright, because the test framework has a builtin concept of service dependency, and abstracts it away from the test code. So the test code itself cannot orchestrate its test dependency (unlike general purpose test frameworks where there is no built-in concept of service dependency)
- When running playwright in Dagger, we want the target service to map to a dagger service - obviously ๐ Let's call that target assignment
- Target assignment cannot be automatically inferred: it requires manual configuration by the workspace owner. Eg. someone has to say "this playwright config must target this dagger service"
- Target assignment should not be done in code. Because we want this use case to be possible without requiring a custom module
- Target assignment should not be done in pseudo-code (configuration that has the complexity of code without the benefits).
- A target assignment says "this playwright config should target this dagger service". Therefore it has 2 parts: a playwright config selector and a service selector.
- The service selector syntax must uniquely identity a dagger service in the workspace in a single string.
- The service selector syntax must be forward-compatible with modules-v2 and in particular with collections (as @south lance mentioned). So
docusaurus:serveon its own is not enough (although maybe it could be a valid subset of the syntax?) - The playwright config selector could be a naive global selector in the beginning (basically a single constructor argument like @south lance showed above). But later will need to allow assigning different service targets to different playwright configs.
I think the hardest part, and the most important part to solve first, is the service selector syntax. Once we have that, we will have a reliable way to designate any service in a single string, without worrying about modules-v2 changes. Then we can figure out where to put that string - that can change over time, it's easier.
(sorry about the wall of text)
The selectors bit is interesting. I need to catch up on the state of the Collections work. The main point is that my selector must return 1 thing. So theoretically if I have 1 docusaurus site in my workspace and the docusarus module has 1 +up function, I could just use docusaurus as my selector
Service selector
I want to add this API to modules-v2 (specifically the Artifacts part, which btw is the first in the rollout)
extend type Workspace {
"""Lookup a service matching the given selector. If no service matches the selector, return null"""
service(selector: String!): Service
}
This ๐ would be a convenience over the richer Workspace.artifacts() URL which allows filtering and querying all artifacts in the workspace - including finding which artifacts have a +up function, and the path to that function.
So we would also extend the artifacts API to support this selector. But for your use case, you would only need Workspace.service()
If you read artifacts.md and collections.md, there's already the selector API. The only thing missing is a syntax to reflect a selecting query in a single string
It's like we already have the plumbing and query engine of sqlite, with an API, but no SQL syntax parser ๐
My best candidate for selector syntax is CEL -> https://github.com/google/cel-go
Could probably be useful in other places. Since it will be able to match any artifact, or specific action on an artifact - not just up
yeah makes sense to me!
If you have this workspace:
type PlaywrightConfigs {
keys: []WorkspacePath!
get(key: WorkspacePath!): PlaywrightConfig
} @collection
type PlaywrightConfig {
run: Void @check
}
type DocusaurusSites {
keys: []WorkspacePath!
get(key: WorkspacePath!): DocusaurusConfig
} @collection
type DocusaurusSite {
devServer: Service! @up
}
Then the existing spec makes these commands possible:
$ dagger list docusaurus-sites
$ dagger list playwright-configs
$ dagger check --playwright-config=./e2e/docs-tests
$ dagger up --docusaurus-site=./docs
$ dagger up --docusaurus-site=./docs dev-server
So we could add a selector like:
$ dagger check --select="playwrightConfig == './e2e/docs-test'"
$ dagger up --select="docusaurusSite == './docs'"
$ dagger up --select="docusaurusSite == './docs' && fn == 'devServer''
From there, we would reuse the same selector syntax where it's needed.
For simple global config:
[modules.playwright]
config.defaultTarget = "docusaurusSite == './docs'"
For playwright-specific config:
import { defineConfig } from '@playwright/test';
export default defineConfig({
dagger: {
target: "docusaurusSite == './docs' && fn == 'devServer'",
},
use: {
baseURL: 'http://localhost:3000',
},
testDir: './tests',
});
Nice so then I come up with a way to put that selector in the playwright config or package.json, and then I can workspace.select(selector) and get my Service to bind
if we're ok with selectors in config.toml that's probably easier than made up keys in existing configs imo
since the selectors are relative to the other members of the same config.toml
Well the selectors are relative to the current workspace.
So they make sense in any file within the workspace
relative was the wrong word, but what i meant is that they're shared concerns
Tried an example snippet of piggybacking on playwright config above ๐
in this case I think it'll be hard to retrieve that value from a typescript file. We'd probably end up with a regex and if thats the case it could just as easily be a comment ๐คท
package.json is easier though. Something like { 'daggerService': 'docusaurusSite == "./docs" && fn == "devServer"' }
But is it guaranteed that there will be a package.json next to that playwright.config.ts ?
According to claude, yes thats safe to assume
Based on your knowledge of playwright can it be assumed that a
playwright.config.tswill
always be alongside apackage.json?โบ Yes. Playwright is an npm package โ you need @playwright/test in a package.json to use it. And playwright.config.ts is conventionally placed at the npm project root, same directory as package.json. There's no supported way to run Playwright without an npm project around it.
OK then ๐
But is there a possibility that it will be the package.json for the app being tested?
That would make it weird in that case
Anyway this part is much more manageable. That's exactly what I mean when I said the service selector part is harder. I'm much less worried about us finding the right place to map to the playwright config (and change over time if needed)