Enable systemd-homed
[workstation-ostree-config.git] / comps-sync.py
1 #!/usr/bin/python3
2 # Usage: ./comps-sync.py /path/to/comps-f34.xml.in
3 #
4 # Can both remove packages from the manifest
5 # which are not mentioned in comps, and add packages from
6 # comps.
7
8 import os, sys, subprocess, argparse, shlex, json, yaml, re
9 import libcomps
10
11 def fatal(msg):
12     print >>sys.stderr, msg
13     sys.exit(1)
14
15 def format_pkgtype(n):
16     if n == libcomps.PACKAGE_TYPE_DEFAULT:
17         return 'default'
18     elif n == libcomps.PACKAGE_TYPE_MANDATORY:
19         return 'mandatory'
20     else:
21         assert False
22
23 def write_manifest(fpath, pkgs, include=None):
24     with open(fpath, 'w') as f:
25         f.write("# DO NOT EDIT! This content is generated from comps-sync.py\n")
26         if include is not None:
27             f.write("include: {}\n".format(include))
28         f.write("packages:\n")
29         for pkg in sorted(pkgs):
30             f.write("  - {}\n".format(pkg))
31         print("Wrote {}".format(fpath))
32
33 parser = argparse.ArgumentParser()
34 parser.add_argument("--save", help="Write changes", action='store_true')
35 parser.add_argument("src", help="Source path")
36
37 args = parser.parse_args()
38
39 print("Syncing packages common to all desktops:")
40
41 base_pkgs_path = 'fedora-common-ostree-pkgs.yaml'
42 with open(base_pkgs_path) as f:
43     manifest = yaml.safe_load(f)
44 manifest_packages = set(manifest['packages'])
45
46 with open('comps-sync-exclude-list.yml') as f:
47     doc = yaml.safe_load(f)
48     comps_exclude_list = doc['exclude_list']
49     comps_include_list = doc['include_list']
50     comps_exclude_list_groups = doc['exclude_list_groups']
51     comps_desktop_exclude_list = doc['desktop_exclude_list']
52     comps_exclude_list_all = [re.compile(x) for x in doc['exclude_list_all_regexp']]
53
54 def is_exclude_listed(pkgname):
55     for br in comps_exclude_list_all:
56         if br.match(pkgname):
57             return True
58     return False
59
60 # Parse comps, and build up a set of all packages so we
61 # can find packages not listed in comps *at all*, beyond
62 # just the workstation environment.
63 comps = libcomps.Comps()
64 comps.fromxml_f(args.src)
65
66 # Parse the workstation-product environment, gathering
67 # default or mandatory packages.
68 ws_env_name = 'workstation-product-environment'
69 ws_ostree_name = 'workstation-ostree-support'
70 ws_environ = comps.environments[ws_env_name]
71 ws_pkgs = {}
72 for gid in ws_environ.group_ids:
73     group = comps.groups_match(id=gid.name)[0]
74     if gid.name in comps_exclude_list_groups:
75         continue
76     exclude_list = comps_exclude_list.get(gid.name, set())
77     for pkg in group.packages:
78         pkgname = pkg.name
79         if pkg.type not in (libcomps.PACKAGE_TYPE_DEFAULT,
80                             libcomps.PACKAGE_TYPE_MANDATORY):
81             continue
82         if pkgname in exclude_list or is_exclude_listed(pkgname):
83             continue
84         pkgdata = ws_pkgs.get(pkgname)
85         if pkgdata is None:
86             ws_pkgs[pkgname] = pkgdata = (pkg.type, set([gid.name]))
87         if (pkgdata[0] == libcomps.PACKAGE_TYPE_DEFAULT and
88             pkg.type == libcomps.PACKAGE_TYPE_MANDATORY):
89             ws_pkgs[pkgname] = pkgdata = (pkg.type, pkgdata[1])
90         pkgdata[1].add(gid.name)
91
92 ws_ostree_pkgs = set()
93 for pkg in comps.groups_match(id=ws_ostree_name)[0].packages:
94     if not is_exclude_listed(pkg.name):
95         ws_ostree_pkgs.add(pkg.name)
96
97 comps_unknown = set()
98 for pkg in manifest_packages:
99     if (pkg not in comps_include_list and
100         pkg not in ws_pkgs and
101         pkg not in ws_ostree_pkgs):
102         comps_unknown.add(pkg)
103
104 # Look for packages in the manifest but not in comps at all
105 n_manifest_new = len(comps_unknown)
106 if n_manifest_new == 0:
107     print("  - All manifest packages are already listed in comps.")
108 else:
109     print("  - {} packages not in {}:".format(n_manifest_new, ws_env_name))
110     for pkg in sorted(comps_unknown):
111         print('    {}'.format(pkg))
112         manifest_packages.remove(pkg)
113
114 # Look for packages in workstation but not in the manifest
115 ws_added = {}
116 for (pkg,data) in ws_pkgs.items():
117     if pkg not in manifest_packages:
118         ws_added[pkg] = data
119         manifest_packages.add(pkg)
120
121 n_comps_new = len(ws_added)
122 if n_comps_new == 0:
123     print("  - All comps packages are already listed in manifest.")
124 else:
125     print("  - {} packages not in manifest:".format(n_comps_new))
126     for pkg in sorted(ws_added):
127         (req, groups) = ws_added[pkg]
128         print('    {} ({}, groups: {})'.format(pkg, format_pkgtype(req), ', '.join(groups)))
129
130 if (n_manifest_new > 0 or n_comps_new > 0) and args.save:
131     write_manifest(base_pkgs_path, manifest_packages)
132
133 # Generate treefiles for all desktops
134 for desktop in [ 'gnome-desktop', 'kde-desktop', 'xfce-desktop',
135         'lxqt-desktop', 'deepin-desktop', 'pantheon-desktop', 'mate-desktop']:
136     print()
137     print("Syncing packages for {}:".format(desktop))
138
139     manifest_path = '{}-pkgs.yaml'.format(desktop)
140     with open(manifest_path) as f:
141         manifest = yaml.safe_load(f)
142     manifest_packages = set(manifest['packages'])
143
144     # Filter packages in the comps desktop group using the exclude_list
145     comps_group_pkgs = set()
146     for pkg in comps.groups_match(id=desktop)[0].packages:
147         pkgname = pkg.name
148         exclude_list = comps_desktop_exclude_list.get(desktop, set())
149         if pkgname in exclude_list or is_exclude_listed(pkgname):
150             continue
151         comps_group_pkgs.add(pkg.name)
152
153     # Look for packages in the manifest but not in comps group
154     comps_unknown = set()
155     for pkg in manifest_packages:
156         if pkg not in comps_group_pkgs:
157             comps_unknown.add(pkg)
158
159     n_manifest_new = len(comps_unknown)
160     if n_manifest_new == 0:
161         print("  - All manifest packages are already listed in comps.")
162     else:
163         print("  - {} packages not in {} comps group:".format(n_manifest_new, desktop))
164         for pkg in sorted(comps_unknown):
165             print('    {}'.format(pkg))
166             manifest_packages.remove(pkg)
167
168     # Look for packages in comps but not in the manifest
169     desktop_pkgs_added = set()
170     for pkg in comps_group_pkgs:
171         if pkg not in manifest_packages:
172             desktop_pkgs_added.add(pkg)
173
174     n_comps_new = len(desktop_pkgs_added)
175     if n_comps_new == 0:
176         print("  - All comps packages are already listed in manifest.")
177     else:
178         print("  - {} packages not in {} manifest:".format(n_comps_new, desktop))
179         for pkg in sorted(desktop_pkgs_added):
180             print('    {}'.format(pkg))
181             manifest_packages.add(pkg)
182
183     # Update manifest
184     if (n_manifest_new > 0 or n_comps_new > 0) and args.save:
185         write_manifest(manifest_path, manifest_packages, include="fedora-common-ostree.yaml")