Layers

A layer is a collection of projects. It typically corresponds to a workspace; a set of related projects being worked on together. Layers may import one or more other layers, which makes the projects defined in the imported layers available to depend upon.

A layer is associated with a working directory by means of a file, .context.fury in that directory.

Referencing layers

A layer is also Fury’s unit of distribution, and a single layer may be stored, and referenced by a hash of its content using the fury:// URL scheme, for example,

fury://Pr4N8MsW375wP4HyXvp8XeCxgWbr9kbB5okVnkC1xmut

This hash uniquely resolves to an immutable definition of the build.

Fury also allows layers to be referenced by more memorable names, such as,

fury://acme.com/platform/v0.1

which corresponds to the layer called platform, made available through a service provided through the acme.com domain name, and with the tag v0.1. The third-party service can decide the structure of layer references, however their names are limited to,

This address will resolve to a hash of the layer, which may or may not be immutable: it depends on the policies of the third-party service, in this example, acme.com.

If the user has configured a default layer service, such as furore.dev, a layer may be specified as simply,

platform/v0.1

The command to import a layer is,

fury layer import -l <layer ref>

Every imported layer must be given a name within the layer importing it. Where a layer has been imported by reference, a default name can be chosen by Fury. For identifiers containing /s, the penultimate slashed part between the slashes will be chosen, or if the layer reference does not include a / character, the entire identifier will be used.

For layers imported by hash, an identifer must be specified with the —name (or -n) argument, for example,

fury layer import -l fury://Pr4N8MsW375wP4HyXvp8XeCxgWbr9kbB5okVnkC1xmut -n example

Referencing schemas

Layers may often consist of several schemas, which provide variations of a set of projects, but which are distributed together.

If the schema to import is not explicitly specified, Fury always imports one particular schema, based on the cases,

  1. if the imported layer has only a single schema, it will be imported, or

  2. if the imported layer has multiple schemas, and one has the same name as the current schema of the current layer, it will be imported

Note that the normal rules of modifying schemas apply, here: for each schema in the current layer, in case (1) above, the same schema would be imported into every one of the schemas in the current layer, and in case (2), the names of schemas in the current layer would be matched against the names of schemas in the imported layer, and imports created appropriately.

If a matching cannot be found, the schema to be imported should be explicitly specified, and this is done by appending the schema name, prefixed with @ to the end of the import command. For example,

fury layer import -l propensive/magnoli/v0.9@scala-2.12

or,

fury layer import -l fury://Pr4N8MsW375wP4HyXvp8XeCxgWbr9kbB5okVnkC1xmut@scala-js -n example

Creating a layer

To start working with a layer, a new one can be initialized using,

fury layer init

which will create a .context.fury file in the current directory, and will create a completely empty layer containing no projects or any other details.

This may be useful in some cases, but some work will be required to specify all the necessary configuration details to be useful. For most projects, a more useful starting point is to clone an existing layer which already specifies enough details to get started with a build, quickly.

To clone a layer, run fury layer clone, specifying the layer reference using, —layer (or -l) parameter, for example,

fury layer clone -l magnolia/v0.9.0

Layer hierarchies

A layer can import projects from other layers. This makes modules of the imported projects available as dependencies, but does not allow direct editing of the projects from dependent layers. The imported projects do not, for example, appear in the list of projects visible when calling,

fury project list

Layers import from other layers, and those layers may themselves import from other layers. Thus, imports of layers form a tree.

Regardless of whether layers are imported by name or by hash, references to imported layers will always be stored in the build model as a hash. This means that a specification for a layer will always import from an exact immutable definition of another layer, and not a variant or more recent version of the layer.

Layer paths

The nature of imported layers forming a tree-structure, each with a unique name, enables nested layers to be referred to by path, for example, if we were to import a layer with the name, community, which imports another layer with the name, typelevel, which itself imports a layer with the name shapeless, then we could refer to the shapeless layer by the path,

./community/typelevel/shapeless

The path to the base layer is always simply ., and layer paths must be specified in full, starting with ./.

Working with layer hierarchies

When maintaining a build, It is often useful to make changes not just to the base layer, but also to the layers it imports. Fury makes this very simple, by allowing the context to be changed to point to a nested layer, specified by path.

To change context to a nested layer, use the fury layer select command, for example,

fury layer select -l ./community/typelevel

We call the layer which is currently in context the “current layer”, and the layer associated with the working directory, which refers to the current layer, directly or indirectly, the “base layer”.

With the context changed, any Fury commands which display or modify the layer will now operate as if they were working directly on the current layer. Once the current layer has been modified, or just read, the base layer can be reselected using,

fury layer select -l .

Updating hashes

When modifying nested layers, every alteration to any aspect of the current layer will also change its hash (simply because its serialized content changes). The layer which imports the current layer will need to be updated to point to the new, updated hash, and this will cause its hash to change too, and consequently every layer hash between the current layer and the base layer will need to change.

Fury handles this automatically, so the updates are transparent.

Updating layers

Layers may be imported by reference, which will be resolved to a hash at the time of import, but the reference may change over time as newer versions of the layer are published. Maintaining a layer involves keeping the build up-to-date, and Fury makes it possible to automatically check and update layer references to the versions currently published.

fury layer update -l ./community

or,

fury layer update -a

to update all imported layers.

Data model

In the Fury build model, a layer is defined as,

Layer {
  version: Int
  schemas: Schema*
  aliases: Alias*
}

The version integer specifies the version of the model. The current latest version is 4, but any published changes to the Fury data model will involve incrementing the version.

Most further details relating to the build definition are specified in one or more schemas in the schemas field, and aliases are specified in the aliases field.

While the Fury user experience presents projects as the members of layers, this hides the detail that one or more schemas may exist which represent variations of the projects.