How to add a custom source type to an application¶
An application may need additional source types not included in craft-parts by default. In this case, you can create and register a new non-default source handler.
Warning
New source handlers may be added in minor releases of craft-parts. If a
custom source handler shares a source_type
value with a new source
handler, registering a custom source handler will override the existing
source handler.
Some source types are considered mandatory and cannot be overridden or unregistered. In a major release, the set of mandatory source types may change. At that point, an application-defined source type may not override the mandatory source type.
Write a new source handler¶
The first step in adding a new source handler is to write one. The components
of a source handler are its source model (a Pydantic
model that defines the source-*
keys that may be used in a part) and the
handler, a class that defines how the source type behaves during the PULL
step.
The Pydantic model is a child class of
BaseSourceModel
. The only mandatory field
is source_type
.
class RsyncDirectorySourceModel(sources.BaseSourceModel, frozen=True):
pattern = "^rsync://"
source_type: Literal["rsync"] = "rsync"
The pattern
attribute
allows Craft Parts to infer the source based on a regular expression. The first
source handler with a matching regular expression will be used, with built-in
source types matching before externally registered source types.
Once this is defined, a SourceHandler
is
needed to define the PULL
behaviour of the source type.
class RsyncSource(sources.SourceHandler):
source_model = RsyncDirectorySourceModel
@override
def pull(self) -> None:
self._run(
[
"rsync",
"--archive",
"--delete",
self.source,
self.part_src_dir.as_posix(),
]
)
Note
Craft Parts does not install any required tools for custom source handlers. The handler in this example will fail on a machine that does not have rsync installed before the part is pulled.
Register the source handler¶
Once created, a source must be registered to be used. This must occur before
entering the ExecutionContext
with
the LifecycleManager
’s
action_executor()
method.
sources.register(RsyncSource)
Run the lifecycle¶
With those steps completed, the new source handler is ready to use. A parts.yaml
file such as the following will use the example rsync source type:
parts:
my-part:
plugin: make
source: rsync://code-host.internal/my-app
After loading the parts into a YAML structure, all that’s left is to run it:
lcm = LifecycleManager(
parts,
application_name="rsync_parts",
cache_dir=pathlib.Path.home() / ".cache",
)
with lcm.action_executor() as ctx:
ctx.execute(Action("my-part", Step.PULL))