Defining modules in modulemd

Simply put, modulemd is a file that defines which packages get built for which releases. It includes a summary and a description, a list of source RPM packages, build information i.e. build order and macros, and usage information i.e. installation profiles and licenses.

A typical modulemd example

A typical modulemd file looks similar to the following examples. Read on for more details about each part of the modulemd file.

Feel free to copy/paste this example when creating your new module.

document: modulemd-packager
version: 3
data:
    # === Information about this module ==================================
    # (Can be copied from the main RPM package, but doesn't need to be)
    summary: An example module
    description: >-
        A module for the demonstration of the metadata format. Also,
        it can span multiple lines.

    # === License of this modulemd file ==================================
    license:
      - MIT
    # === Module context configurations ===========================================
    # (For which Fedora releases to build?)
    configurations:
        # context:
        # A string of up to ten [a-zA-Z0-9] characters representing a
        # a build and runtime configuration for this stream. This string is
        # arbitrary but must be unique in this module stream.
        # Type: MANDATORY
      - context: CTX1
          # platform:
          # Defines the distribution and release to build on and run against.
          # Type: MANDATORY
        platform: f32
          # buildrequires:
          # A dictionary of the build-time dependencies on other module streams.
          # Each configuration may depend on a single stream of a dependency.
          # The dictionary key is the name of the module and the dictionary value
          # is a single-element list containing the name of the stream.
          #
          # Type: Optional
        buildrequires:
          appframework: [v1]
          # requires:
          # A dictionary of the run-time dependencies on other module streams.
          # Each configuration may depend on a single stream of a dependency.
          # The dictionary key is the name of the module and the dictionary value
          # is a single-element list containing the name of the stream.
          #
          # Type: Optional
        requires:
          appframework: [v1]
          # buildopts:
          # Component build options
          # Additional per component type module-wide build options.
          #
          # IMPORTANT: Due to limitations in the modulemd-stream v2 format, the
          # buildopts from the first configuration in the list will apply to
          # ALL configurations when building for modulemd-stream v2. They will
          # apply separately when building for module-stream v3.
          #
          # Type: OPTIONAL
        buildopts:
            # rpms:
            # RPM-specific build options
            #
            # Type: OPTIONAL
          rpms:
                # macros:
                # Additional macros that should be defined in the
                # RPM buildroot, appended to the default set.  Care should be
                # taken so that the newlines are preserved.  Literal style
                # block is recommended, with or without the trailing newline.
                #
                # Type: OPTIONAL
            macros: |
              %demomacro 1
              %demomacro2 %{demomacro}23

            # whitelist:
            # Explicit list of package build names this module will produce.
            # By default the build system only allows components listed under
            # data.components.rpms to be built as part of this module.
            # In case the expected RPM build names do not match the component
            # names, the list can be defined here.
            # This list overrides rather then just extends the default.
            # List of package build names without versions.
            #
            # Type: OPTIONAL
            whitelist:
              - fooscl-1-bar
              - fooscl-1-baz
              - xxx
              - xyz
            # arches:
            # Instructs the build system to only build the
            # module on this specific set of architectures.
            # Includes specific hardware architectures, not families.
            # See the data.arch field in the modulemd-stream spec for details.
            # Defaults to all available arches.
            #
            # Type: OPTIONAL
            arches: [i686, x86_64]

        # Alternate example with no dependencies
      - context: CTX2
        platform: f33
    # === Module API (optional, but encouraged) ==========================
    # (Which packages are API-stable?)
    api:
        rpms:
            - package-one        # <- Binary RPM package name
            - package-one-extras # <- Binary RPM package name
            - package-one-cli    # <- Binary RPM package name
            - package-one-devel  # <- Binary RPM package name
            - package-two        # <- Binary RPM package name

    # === Package filtering ==============================================
    # (Which packages should not be included into the resulting module)
    filter:
        rpms:
            - subpackage-one     # <- Binary RPM package name

    # === Installation profiles (optional, but encouraged) ===============
    # (Helping users with installation by providing predefined groups)
    profiles:
        default:  # <- Name of the profile
            description: A standard installation.
            rpms:
                - package-one         # <- Binary RPM package name
                - package-one-extras  # <- Binary RPM package name
                - package-two         # <- Binary RPM package name
        cli:      # <- Name of the profile
            description: A command-line client.
            rpms:
                - package-one-cli     # <- Binary RPM package name

    # === Packages in this module ========================================
    # (Referenced by their dist-git repo name + branch name)
    components:
        rpms:
            first-package:  # <- Source RPM package name
                ref: 3.0    # <- Branch name in dist-git
                rationale: Provides the core functionality.
            second-package: # <- Source RPM package name
                ref: latest # <- Branch name in dist-git
                rationale: Web UI for the first-package.

Common modulemd definitions

These are the common parts of a modulemd file, used in the example above. Advanced definitions, including a complex example of Module Stream Expansion (MSE), are towards the end of this page.

Document header

Every modulemd starts with these three lines:

document: modulemd-packager
version: 3
data:
    ... (1)
1 All the following definitions go here, under data.

Information about this module

Tell users what this module represents by writing a summary and a description.

    summary: An example module
    description: >-  (1)
        A module for the demonstration of the metadata format. Also,
        it can span multiple lines.
1 The >- means new line in YAML. Useful for longer blocks of text, such as the description!

License of this modulemd file

This is a license of this very modulemd file and it doesn’t need to be modified. The build system adds licenses of all packages to this list automatically.

    license:
      - MIT  (1)
1 A license for this modulemd file. Fedora content, such as SPEC files or patches not included upstream, uses the MIT license by default, unless the component packager declares otherwise.

Module context configurations

Each context configuration describes how a module stream and its components should be build and run. A single-context example:

    configurations:
      - context: CTX1   (1)
        platform: f32   (2)
        buildrequires:
          appframework: [v1]    (3)
        requires:
          appframework: [v1]    (4)
1 A context name for the single configuration.
2 An identifier of a major release of a distribution this context will be built and it will run on.
3 Module streams to enable when building this context (build-time modular dependencies).
4 Module streams to enable when installing this context (run-time modular dependencies).

The context field is mandatory and must be unique inside a single stream. The value is an arbitrary string with a limited length and alphabet. The context value is used by DNF for establishing an upgrade path among multiple module versions and contexts. Thus once the value is set, it should not change.

The platform identifier is mandatory and it matches a Fedora release. E.g. f32 means Fedora 32. Rawhide also uses a numerical value. Technically, it’s a stream of platform module.

Modular dependencies at run time can differ from those at build time. There can be multiple modules listed, but always exactly one stream. E.g. appframework: [v1] means appframework:v1 module stream.

Here is a complex example with multiple contexts:

    configurations:
      - context: CTX1
        platform: f32
        buildrequires:
          appframework: [v1]
        requires:
          appframework: [v1]
          frameworkext: [v3]
      - context: CTX2
        platform: f33
        buildrequires:
          appframework: [v1]
        requires:
          appframework: [v1]
          frameworkext: [v3]
      - context: CTX3
        platform: f33
        buildrequires:
          appframework: [v2]
        requires:
          appframework: [v2]

This example will produce one module build for Fedora 32 and two builds for Fedora 33. The Fedora 32 module will depend on appframework:v1 and frameworkext:v3, while Fedora 33 module will support both appframework:v1 and appframework:v2 streams. The context CTX3 does not depenend on frameworkext:v3 because it was merged into appframework:v2.

Installation profiles (optional, but encouraged)

To help users install your module, define installation profiles. These profiles represent a specific use case of your module. Most modules have at least a default profile. But you can specify more. For example, a database module can have a server and a client profile.

    profiles:
        default:  (1)
            description: A standard installation.  (2)
            rpms:
                - package-one         (3)
                - package-one-extras  (3)
                - package-two         (3)
        cli:
            description: A command-line client.
            rpms:
                - package-one-cli
        ...  (4)
1 Name of the profile.
2 A quick summary of the profile.
3 Binary packages to be installed with this profile.
4 List as many profiles as you need.

Module API (optional, but encouraged)

List all binary RPM packages in your module that you consider to be the main stable feature of the module. Other (unlisted) packages should be considered unsupported, or an implementation detail.

    api:
        rpms:
            - package-one
            - package-one-extras
            - package-one-cli
            - package-one-devel
            - package-two

Packages in this module

List all source SRPM packages this module should include, referenced them by their dist-git repo name + branch name.

    components:
        rpms:
            first-package:  (1)
                rationale: Provides the core functionality.  (2)
                ref: 3.0  (3)
            second-package:
                rationale: Web UI for the first-package.
                ref: latest
            ...  (4)
1 Name of the package — maps to a DistGit repository name.
2 The reason why is this package here. Mostly for humans.
3 DistGit branch, tag, or a commit — so the right version of the package gets included.
4 List as many packages as you need.

Advanced definitions

References to the upstream (optional)

You can also provide references to the upstream community, documentation, or to an issue tracker.

    references:
        community: http://www.example.com/  (1)
        documentation: http://www.example.com/  (2)
        tracker: http://www.example.com/  (3)
1 Upstream community website, if it exists.
2 Upstream documentation, if it exists.
3 Upstream bug tracker, if it exists.

Building in a specific order (optional)

Packages are built in batches. By default, all packages are part of a single group, and therefore built concurrently.

To build packages in a specific order, assign them to multiple build groups. Build groups are identified by an integer. Groups with lower number are built first. Negative values are allowed, 0 is the implicit default value.

In this specific example, first-package gets built first, and second-package gets built second.

    components:
        rpms:
            first-package:
                rationale: Provides the core functionality.
                ref: 3.0
                buildorder: 0  (1)
            second-package:
                rationale: Web UI for the first-package.
                ref: latest
                buildorder: 10  (1)
1 A number of the build group.

For even more complex scenarios, please study the modulemd-packager specification.

RPM macros (optional)

RPM packages while being built as part of a module have the following RPM macros available:

%dist .scrmod+f37+14301+76d220e4    (1)
%modularitylabel perl-Module-Install:master:3720220414092112:dd3c6e0e (2)
%_module_build 1    (3)
%_module_name perl-Module-Install   (4)
%_module_stream master  (5)
%_module_version 3720220414092112   (6)
%_module_context dd3c6e0e   (7)
1 A %dist macro of a unique value used in Release RPM specification tags.
2 An RPM tag stored into binary RPM packages. DNF uses it to distinguish modular packages from nonmodular ones.
3 A macro denoting that a modular package is being built.
4 A name of the module being built.
5 A stream of the module being built.
6 A version of the module being built.
7 A context of the module being built.

You can use these macros in RPM specification files of your RPM components to modify building of the packages.

If you need additional RPM macros, you can define them in a buildopts section of your modulemd file:

    buildopts:
        rpms:
            macros: |
                %perl_bootstrap 1

This section belongs into an item of configurations list in case of v3 modulemd format. In case of v2 format it belongs directly into data section.

Filtered Packages (optional, defaults to no filters)

The build process of a RPM packages can result in a subpackages which complement the build of the package (docs, additional build requires etc.). One source RPM package might produce multiple binary RPM packages. Those subpackages are not always desired to be shipped with the Module. Modules enable you to filter out those undesirable packages with the filter build option. After the build is finished the filtered packages will be not included in the artifacts property in the result modulemd yaml file. The artifacts property is added by the build system post build. For an example please refer to the modulemd spec files.

Filtered RPMs are still available to use as build dependencies in subsequent stages of the module build, but are not included in the composed repository for users.

    filter:
        rpms:
            - first-package-debuginfo
            - second-package-nope

Demodularized packages (optional, defaults to none)

If you decide to remove a binary package from your module stream, you will probably stop building it, or you will filter it out with a filter option. But that’s not enough if you want to move the package back to nonmodular packages: Because the package remains listed among artifacts in the previous version of the stream and a package manager could see both the updated and the historical version. (The previous version can be available in GA and updates repositories, while your updated module will first appear in an updates-testing repository.)

To return the package back to nonmodular package set, you need to write it on a list of demodularized packages. A package manager checks the demodularized list of the very latest version of the module stream over all repositories and if it only sees a package name there, it will stop hiding the same-named nonmodular packages while the stream is enabled.

The list of demodularized packages is defined in demodularized field:

    demodularized:
        rpms:
            - first-removed-package
            - second-removed-package

With this explicit mechanism, called demodularization, a package can be demoted from a module. If you ever revert your decision and make the package modular again, the only thing necessary is remove it from the demodularized list.

Creating build-only components (optional)

In addition to filtering subpackages, it’s possible to filter out all of the artifacts produced by a component in a module. This is useful in cases where your module’s primary packages have a build-time dependency that you do not want to ship. An example of such a case would be if you need to build with a specially-patched documentation-generator that would conflict with the version used as the default in Fedora.

    components:
        rpms:
            customdocgen:
                rationale: A patched version of docgen that enables an experimental feature.
                ref: experimental
                buildorder: 0
                buildonly: 1
            myapp:
                rationale: My application
                ref: latest
                buildorder: 10

In this example, customdocgen would be built first and made available in the buildroot for myapp to use during its build. Once the module build is finished and it is composed into a DNF repository, only the unfiltered artifacts from myapp will be available. All of the customdocgen artifacts will be automatically added to the data.filters.rpms section of the module metadata.

A minimal modulemd

An absolute minimum

This module includes two source RPM packages built for the Fedora 35 releases.

document: modulemd-packager
version: 3
data:
    summary: An example module
    description: >-
        A module for the demonstration of the metadata format.
    license:
        - MIT
    configurations:
      - context: CTX1
        platform: f35
    components:
        rpms:
            first-package:
                rationale: Provides the core functionality.
                ref: 3.0
            second-package:
                rationale: Web UI for the first-package.
                ref: latest

This module includes two source RPM packages built for the Fedora 35 releas. It makes clear which packages are considered the API, and helps users with installation thanks to the profiles.

document: modulemd-packager
version: 3
data:
    summary: An example module
    description: >-
        A module for the demonstration of the metadata format.
    license:
        - MIT
    configurations:
      - context: CTX1
        platform: f35
    api:
        rpms:
            - package-one
            - package-one-extras
            - package-one-cli
            - package-one-devel
            - package-two
    profiles:
        default:
            description: A standard installation.
            rpms:
                - package-one
                - package-one-extras
                - package-two
        cli:
            description: A command-line client.
            rpms:
                - package-one-cli
    components:
        rpms:
            first-package:
                rationale: Provides the core functionality.
                ref: 3.0
            second-package:
                rationale: Web UI for the first-package.
                ref: latest