-
Notifications
You must be signed in to change notification settings - Fork 9
HIPPOFACTS and dependencies #20
Comments
I've been noodling on this at the Hippofactory level and I'm concerned that we are going to end up duplicating a lot of stuff from the Bindle logic. The base case, where we want to reference a Wasm module that exists as a parcel in another bindle, but is not present locally, is relatively simple at the technical level. But it feels awkward that the Hippo user needs to know the module structure of the bindle from which they are using a module - what if, in a fit of enthusiasm, you port the static file server from Grain to OCaml, and the serving module changes from And when we start getting into the "what if that module requires other parcels" - well, now we have to parse out that dependency tree and copy all those entries into the application bindle... well, this feels like the client is repeatedly duplicating information that should come in by reference. And as you identify, what about conflicts? Because I shouldn't have to know how the static file server or whatever works, I don't know what files it might need today or in future. In option 3, what if a new version requires a new parcel file that is overwritten by a local file? In option 1, what if a new version requires a new parcel file that I'm not aware of? So I'd like to approach this from the point of view of abstraction. How can I bring in a service implemented in another bindle without knowing the internal implementation details of that service? Can we have something in This could be a client concern. It might also be worth exploring whether this is something that WAGI itself should understand, so that WAGI is able to provide this kind of composition independently of Hippo or HF - I'm not sure if that's within WAGI's remit. But anyway I would like to consider whether we can provide mechanisms for abstraction so that the user is freed from having to know handler implementation. |
In your example, yes, by renaming I don't think we would be requiring knowledge of the bindle, though. Knowledge of the upstream HIPPOFACTS would be sufficient. And on an optimistic note:
Yes, behind the scenes there will be some dependency resolution. Fortunately, it's a "flat" resolution because each bindle contains a definitive list of parcels that it needs. (That is, we don't have to walk an n-deep tree of bindles). I'm not entirely clear if that is your concern, or if you are just worried about n-deep parcel/group trees. But, yes, by design bindles will have internal structure that will need to be respected when using them in HIPPOFACTS. This doesn't differ from any other package management system, though. Conflicts represent only a limited surface area of a bindle. We do have to handle conflicts in the case where any given bindle potentially conflicts with a local file, but only in that one case listed above where the user has to indicate which file they want to use in the
We could absolutely use the
And the rest would be automatically resolved. I'm not sure I see how your proposal would work with the "OR-case" I presented above, where I want some files from the upstream bindle to be made available, but want local overrides of others. But that, I think, could be worked out while still allowing a bindle to declare its default configuration. That said, I do think the original spec gives us desirable flexibility for allowing (a) a bindle dev to specify multiple ways of using their bindle, and (b) a bindle user a high degree of configurability. I think it makes sense to retain those features, while making the simple case really simple.
I am not opposed to Wagi having the ability to do more. BUT, the artifact of record should be a bindle, and a bindle does not have any notion of external dependencies because its dependencies are computed at packaging-time (which makes them actually immutable instead of "immutable by way of a pin file that still has external references that can break when left-pad suddenly disappears from the Interwebs"). The system I originally proposed was designed to render dependencies at |
In a related discussion, it was asked whether we could support identifying a specific file parcel by bindle + name instead of bindle + sha. The bindle + sha format is: |
Another idea, thinking out loud, let's suppose we introduce a parcel annotation or feature, say
Then a HIPPOFACTS file can specify that with something like:
When Hippofactory sees this, it searches the invoice for a parcel with a
I am not sure if this adds anything useful over a single bindle-level None of this really helps me past the issues of require tree chasing and conflict resolution, but I have to go home now, so might try to have a brainstorm with someone about this tomorrow. |
That does make sense to me. |
After talking to @technosophos we have the following plan:
We haven't really tackled the question of what to do about file name clashes if the importee does bring files in by |
That sounds like an excellent plan. That opens a dialogue with end users who need this, and we can form more opinions at that time. |
I figured I would open an issue to brainstorm about how we might express dependencies between a local hippo app and an upstream Wasm module.
Use Case
Imagine I have a simple app. The
HIPPOFACTS
file looks like this:I would like to add the ability to serve static files from my app (at the path
/static/...
). And rather than write that code, I would like to use an existing fileserver. TheHIPPOFACTS
file for that project looks like this:(While the actual artifact we care about is the
invoice.toml
, theHIPPOFACTS
above gives us all the information we could reasonably expect a user to know about a Bindle).So how might I, as a
hippofactory
user, express my desire to use thefileserver
inside of my own app.Option 1: Out-of-band Handling
It is perfectly reasonable to say that the solution to this is that the user figures out how to get a copy of
fileserver.gr.wasm
on their own, download it locally, and include it directly:In this case, the user merely adds the downloaded Wasm module to their
HIPPOFACTS
, and the user takes on all of the responsibilities of managing that module.Option 2: Add Dependencies in
HIPPOFACTS
In this option,
hippofactory
is extended to declare additional dependencies more likeCargo.toml
orpackage.json
. Because bindles are immutable, we can punt here on the entire topic of lockfiles and such and focus just on the DevEx for now.In this case, we allow a user to declare, in
HIPPOFACTS
that the user intends to use parcels located in an existing bindle. One possible syntax for this is:While the exact structure of the
[[dependency]]
object is certainly a wide open area for conversation, the example above illustrates two features that I think are necessary:Let's treat each one separately:
Addressing a Bindle and Parcels
A bindle is composed of one or more parcels organized into groups. When pulling a bindle into an app, the user may have to make some decisions about how that bindle is to be pulled in.
For example, the bindle for our fileserver application has just one Wasm parcel. But a bindle could have several Wasm parcels, each doing a different thing. Bindle's design philosophy makes it possible for one parcel to declare dependencies on other parcels. And it also makes it possible to switch parcels on or off based on group membership or features.
So a key ability when importing a bindle is to be able to specify which parcels you want. And the traditional means of doing so are through specifying groups and features.
dependency
bindle
: Object. The top-level description of a bindlename
: String (REQUIRED). The full name of a bindle, e.g.example.com/foo/1.2.3-alpha.99
groups
: Array. Zero or more group names. Any group listed here is included in full (all parcels) unless a feature flag turns off the parcel.features
: Map<String, String>. Feature name and feature value to enable: (feature.wagi.file
,true
)parcel
: String. The SHA (or we could do thename
, which is probably better) for an exact parcel to pull (maybe not a good idea) If this is specified, groups and features are ignoredexcludeGlobalGroup
: boolean. If set totrue
, the global group will not be imported from the parcel.Given a
dependency.bindle
definition, the runtime should be able to determine what bindle to load, and which parcels to fetch for that bindle.Binding parcels to features
The previous definition gave us a bindle and associated parcels, but it provided no instructions on how those parcels are to be included in the application. My suggestion is that we include a
handler
definition in a dependency, and that this definition matches thehandler
definition for a local object:The fields are the same as those on an existing HIPPOFACTS file, but the following clarications apply:
name
: This refers to the parcel name within the Bindle. As a design constraint on this system, parcel names should be unique.route =
orhost =
), it will override the feature on the imported module. We might need a reserved way of unsetting a value. (e.g.route = "-"
effectively setsroute
to its empty value)An open question: File parcels
Right now files are attached to a handler using the
files
array:When pulling in a bindle and its parcels, it is desirable that we pull in the file parcels attached to it.
But, as the present
fileserver
case illustrates, it may also be desirable to supply files from the local project to be loaded into the external parcel. E.g. if I load afileserver
parcel, it is very likely that I will want to tell that external parcel which of my local files I want it to serve.It seems there are two sets of goals, then:
Here are some example use-cases:
The last case illustrates an intent to "unset" a file that appears in the upstream parcel without replacing it with a local file. E.g. I just don't want a
README.md
at all.Because a handler can easily have hundreds of files attached, it does not seem like manually building a list would be a good approach.
Possible solutions:
files = ['local.txt', 'parcel:README.md']
.files
is specified, use only local files (e.g. all parcels or all locals, no mixing)files = []
appends to the list. When duplicates occur, the local overrides the parcel file.parcelFiles
directive in addition to files:files = ['mylocal.txt']
andparcelFiles = ['README.md']
, with the result being the union of the two (with naming conflicts favoring local)omitParcelFiles
directive that removes parcel files, and default to all parcel files. Then use the same strategy as CI workflows #4 to resolveThe issue I have is that we don't want this process to be burdensome to the user. All of these feel either burdensome or too limiting.
The text was updated successfully, but these errors were encountered: