• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

.beautifier/H09-Jul-2021-7466

.ci/H09-Jul-2021-1,214825

bindings/python/H09-Jul-2021-249173

contrib/H09-Jul-2021-932672

fedora/H09-Jul-2021-280214

modulemd/H09-Jul-2021-100,75978,276

yaml_specs/H09-Jul-2021-2,2232,068

.clang-formatH A D09-Jul-20211.5 KiB6157

.packit.ymlH A D09-Jul-2021440 2419

COPYINGH A D09-Jul-20211.1 KiB105

NEWSH A D09-Jul-20212.2 KiB6045

README.mdH A D09-Jul-202112.6 KiB365295

get_version.shH A D09-Jul-2021164 73

meson.buildH A D03-May-20226.4 KiB221195

setup_dev_container.shH A D09-Jul-2021943 4733

spec.v1.yamlH A D09-Jul-202112.9 KiB293292

spec.v2.yamlH A D09-Jul-202127.6 KiB738673

spec.yamlH A D09-Jul-202127.6 KiB738673

spec_tmpl.shH A D09-Jul-2021269 159

README.md

1![Continuous Integration](https://github.com/fedora-modularity/libmodulemd/workflows/Continuous%20Integration/badge.svg)
2[![Travis](https://img.shields.io/coverity/scan/13739.svg?style=plastic)](https://scan.coverity.com/projects/sgallagher-libmodulemd)
3
4# libmodulemd
5C Library for manipulating module metadata files
6
7Full details can be found in the
8[API Documentation](https://fedora-modularity.github.io/libmodulemd/latest/)
9
10# Using libmodulemd from Python
11Using libmodulemd from Python is possible thanks to the gobject-introspection
12project. To use libmodulemd from Python, include the following at the top of
13your sources.
14
15Install the `python2-libmodulemd` or `python3-libmodulemd` package for your
16system depending on the version of python with which you are working.
17These packages provide the appropriate language bindings.
18
19```python
20import gi
21gi.require_version("Modulemd", "2.0")
22from gi.repository import Modulemd
23```
24
25# Working with repodata (DNF use-case)
26The libmodulemd API provides a number of convenience tools for interacting
27with repodata (that is, streams of YAML that contains information on multiple
28streams, default data and translations). The documentation will use two
29repositories, called "fedora" and "updates" for demonstrative purposes. It
30will assume that the content of the YAML module metadata from those two
31repositories have been loaded into string variables `fedora_yaml` and
32`updates_yaml`, respectively.
33
34First step is to load the metadata from these two repositories into
35ModulemdModuleIndex objects. This is done as follows:
36
37## C
38```C
39ModulemdModuleIndex *fedora_index = modulemd_module_index_new ();
40gboolean ret = modulemd_module_index_update_from_string (
41  fedora_index, fedora_yaml, TRUE, &failures, &error);
42
43ModulemdModuleIndex *updates_index = modulemd_module_index_new ();
44gboolean ret2 = modulemd_module_index_update_from_string (
45  updates_index, updates_yaml, TRUE, &failures, &error);
46```
47
48## Python
49```python
50fedora_index = Modulemd.ModuleIndex.new()
51ret, failures = fedora_index.update_from_string(fedora_yaml, True)
52
53updates_index = Modulemd.ModuleIndex.new()
54ret, failures = updates_index.update_from_string(updates_yaml, True)
55```
56
57The `failures` are a list of subdocuments in the YAML that failed parsing,
58along with the reason they failed. Hence, by checking the return value of
59failures we will know if the YAML parsing was successful or not.
60
61Since it doesn't really make sense to view the contents from separate
62repositories in isolation (in most cases), the next step is to merge the two
63indexes into a combined one:
64
65## C
66```C
67ModulemdModuleIndexMerger *merger = modulemd_module_index_merger_new ();
68
69modulemd_module_index_merger_associate_index (merger, fedora_index, 0);
70modulemd_module_index_merger_associate_index (merger, updates_index, 0);
71
72ModulemdModuleIndex *merged_index =
73  modulemd_module_index_merger_resolve (merger, &error);
74```
75
76## Python
77```python
78merger = Modulemd.ModuleIndexMerger.new()
79
80merger.associate_index(fedora_index, 0)
81merger.associate_index(updates_index, 0)
82
83merged_index = merger.resolve()
84```
85
86At this point, you now have either a complete view of the merged repodata,
87or else have received an error describing why the merge was unable to
88complete successfully. Additionally, it should be noted that the combined
89metadata in any ModulemdModuleIndex will have all of its component parts
90upgraded to match the highest version of those objects seen. So for example
91if the repodata has a mix of v1 and v2 ModulemdModuleStream objects, the
92index will contain only v2 objects (with the v1 objects automatically
93upgraded internally).
94
95Now, we can start operating on the retrieved data. This guide will
96give only a brief overview of the most common operations. See the API
97specification for a full list of information that can be retrieved.
98
99## Discover the default stream for a particular module.
100
101## C
102```C
103ModulemdModule *module =
104  modulemd_module_index_get_module (merged_index, "modulename");
105ModulemdDefaults *defaults = modulemd_module_get_defaults (module);
106printf ("Default stream for modulename is %s\n",
107        modulemd_defaults_v1_get_default_stream (
108          MODULEMD_DEFAULTS_V1 (defaults), NULL));
109```
110
111## Python
112```python
113module = merged_index.get_module("modulename")
114defaults = module.get_defaults()
115print("Default stream for modulename is %s" % defaults.get_default_stream())
116```
117
118## Get the list of RPMs defining the public API for a particular module NSVCA
119First, query the ModulemdModuleIndex for the module with a given name.
120
121## C
122```C
123ModulemdModule *module =
124  modulemd_module_index_get_module (merged_index, "modulename");
125```
126
127## Python
128```python
129module = merged_index.get_module("modulename")
130```
131
132Then, query the ModulemdModule for the ModulemdModuleStream associated with the
133provided NSVCA (name-stream-version-context-architecture identifier).
134
135## C
136```C
137ModulemdModuleStream *stream = modulemd_module_get_stream_by_NSVCA (
138  module, "modulestream", 0, "deadbeef", "coolarch", &error);
139```
140
141## Python
142```python
143stream = module.get_stream_by_NSVCA("modulestream", 0, "deadbeef", "coolarch")
144```
145
146Lastly, read the RPM API from the ModulemdModuleStream. Here, `api_list` is
147a list of strings containing package names.
148
149## C
150```C
151GStrv api_list = modulemd_module_stream_v2_get_rpm_api_as_strv (
152  MODULEMD_MODULE_STREAM_V2 (stream));
153```
154
155## Python
156```python
157api_list = stream.get_rpm_api()
158```
159
160Also note that in addition to accessor API methods, many objects also have
161properties that can be accessed directly.
162
163## C
164```C
165printf ("Documentation for module stream is at %s\n",
166        modulemd_module_stream_v2_get_documentation (
167          MODULEMD_MODULE_STREAM_V2 (stream)));
168g_autofree gchar *doc;
169g_object_get (MODULEMD_MODULE_STREAM_V2 (stream), "documentation", &doc, NULL);
170printf ("Documentation for module stream is at %s\n", doc);
171```
172
173## Python
174```python
175print("Documentation for module stream is at %s" % stream.get_documentation())
176print("Documentation for module stream is at %s" % stream.props.documentation)
177```
178
179## Retrieve the modular runtime dependencies for a particular module NSVCA
180
181## C
182```C
183ModulemdModule *module =
184  modulemd_module_index_get_module (merged_index, "modulename");
185ModulemdModuleStream *stream = modulemd_module_get_stream_by_NSVCA (
186  module, "modulestream", 0, "deadbeef", "coolarch", &error);
187GPtrArray *deps_list = modulemd_module_stream_v2_get_dependencies (
188  MODULEMD_MODULE_STREAM_V2 (stream));
189
190for (gint i = 0; i < deps_list->len; i++)
191  {
192    GStrv depmodules_list =
193     modulemd_dependencies_get_runtime_modules_as_strv (
194       g_ptr_array_index (deps_list, i));
195
196    for (gint j = 0; j < g_strv_length (depmodules_list); j++)
197      {
198        GStrv depstreams_list =
199          modulemd_dependencies_get_runtime_streams_as_strv (
200            g_ptr_array_index (deps_list, i), depmodules_list[j]);
201
202        for (gint k = 0; k < g_strv_length (depstreams_list); k++)
203          {
204            // do stuff with depmodules_list[j], depstreams_list[k]
205          }
206      }
207  }
208```
209
210## Python
211```python
212module = merged_index.get_module("modulename")
213stream = module.get_stream_by_NSVCA("modulestream", 0, "deadbeef", "coolarch")
214deps_list = stream.get_dependencies()
215for dep in deps_list:
216    depmodules_list = dep.get_runtime_modules()
217    for depmod in depmodules_list:
218        depstream_list = dep.get_runtime_streams(depmod)
219        for depstream in depstream_list:
220            # do stuff with depmod, depstream
221```
222
223# Working with a single module stream (Packager/MBS use-case)
224One limitation of the ModulemdModuleIndex format is that it requires that
225all module streams loaded into it have both a name and a stream name.
226This however is not possible when dealing with streams such as a packager
227would be using (since the build-system auto-generates the module name and
228stream name from the git repository information. In this case, we need to
229work with a single module stream document at a time. For this, we will
230use the ModulemdModuleStream interface.
231
232This example will assume that the module name and stream name have
233already been determined from the repodata and that they are stored in
234string variables named `module_name` and `stream_name`, respectively.
235
236## Python
237```python
238stream = Modulemd.ModuleStream.read_file(
239    "/path/to/module_name.yaml", True, module_name, stream_name
240)
241v2_stream = stream.upgrade(Modulemd.ModuleStreamVersionEnum.TWO)
242v2_stream.validate()
243```
244In the example above, we upgraded the stream to v2, in case we were reading
245from v1 metadata. This will allow us to avoid having to manage multiple
246code-paths and support only the latest we understand. After that, it calls
247validate() to ensure that the content that was read in was valid both
248syntactically and referentially.
249
250Also available is `Modulemd.ModuleStreamVersionEnum.LATEST` which will
251always represent the highest-supported version of the
252ModulemdModuleStream metadata format. This may change at any time.
253
254# Getting started with developing
255
256## Forking and cloning the sources
257The libmodulemd project follows the
258[Github Fork-and-Pull](https://reflectoring.io/github-fork-and-pull/) model of
259development. To get started, create a fork of the upstream libmodulemd sources,
260clone those locally and create branches on your fork to make changes. When they
261are ready for review or feedback, create a pull-request.
262
263## Prerequisites
264* A development system with either [Podman](https://podman.io/) and
265[Buildah](https://buildah.io/) (preferred) or
266[Docker](https://www.docker.com/) installed.
267
268## Preparing a build environment
269Create a container for building the libmodulemd sources. Run the handy setup
270script in the root of the checkout.
271```
272./setup_dev_container.sh [<Fedora Release>]
273```
274If unspecified, it will default to "Fedora Rawhide". To select a different
275Fedora release for the base image, add the release number as an argument to the
276command. For example:
277```
278./setup_dev_container.sh 33
279```
280
281This will automatically pull down a Fedora base image, install all of the
282packages needed for development and testing and then provide you with a shell
283inside this environment.
284
285## Building the sources
286Projects built with the meson build-system require a separate build directory from
287the source path. The `meson` command will generate this directory for you.
288```
289meson --buildtype=debug -Db_coverage=true debugbuild
290```
291The above command (run from the root of the source checkout) will create a new
292subdirectory - `debugbuild` - configured to compile with debug symbols and
293`gcov` symbols to measure test coverage.
294
295To build the sources, `chdir()` into the `debugbuild` directory and run
296```
297ninja
298```
299
300To build and run the in-tree tests, use
301```
302ninja test
303```
304
305To generate HTML documentation, you can run
306```
307ninja modulemd-2.0-doc
308```
309(Be aware that the GLib documentation module in meson has some strange quirks
310and won't recognize newly-added pages without deleting and re-creating the
311build directory first.)
312
313
314## Running more advanced test suites
315In addition to the basic `ninja test` set of tests, libmodulemd also has a
316suite of tests performed in the Github Actions environment. These are also
317containerized and can be run locally on the host. (You will not be able to run
318them from within the development container shell, as nested containers are not
319supported.)
320
321To run the container-based tests, you can run the following from the source
322root:
323```
324./.ci/ci-fedora.sh
325```
326(Optionally, you can pass the release number as an argument to switch to
327building and testing against that release rather than Fedora Rawhide).
328
329Support for running the tests on other OSes is ongoing. See the `.ci`
330directory for available suites. All supported OSes and release versions are
331tested as part of each pull request.
332
333
334## Tips and tricks
335
336### Running tests in debug mode
337
338The libmodulemd library is built atop
339[GObject](https://developer.gnome.org/gobject/stable/). It provides a debug
340mode that is configurable by an environment variable. In general, it is highly
341recommended that you run all tests with
342`G_DEBUG='fatal-warnings,fatal-criticals'` set in the environment. This will
343cause the application to `abort()` on programming errors that would be logged
344and ignored at runtime.
345
346
347### Running tests with valgrind
348Assuming your current working directory is `debugbuild` as described above:
349```
350meson test --suite=ci_valgrind --wrap=../contrib/valgrind/valgrind_wrapper.sh
351```
352
353If not, you may need to adjust the path to libmodulemd-python.supp.
354
355You can also specify individual tests to run against. See `meson test --list`
356for the available tests.
357
358The automated CI tests will always run with valgrind on all platforms where it
359is supported.
360
361# Authors:
362* Stephen Gallagher <sgallagh@redhat.com>
363* Merlin Mathesius <mmathesi@redhat.com>
364* Igor Gnatenko <ignatenkobrain@fedoraproject.org>
365