Include TUF scripts in this repo.
[bitmask_bundler.git] / tuf / release.py
1 #!/usr/bin/env python
2 # release.py
3 # Copyright (C) 2014 LEAP
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 """
19 Tool to generate TUF related files after a release
20
21 The 'repo' folder should contain two folders:
22   - 'metadata.staged' with all the jsons from the previows release
23   - 'targets' where the release targets are
24 """
25
26 import datetime
27 import os.path
28 import sys
29
30 from tuf.repository_tool import load_repository
31 from tuf.repository_tool import import_rsa_privatekey_from_file
32
33 """
34 Days until the expiration of targets.json and snapshot.json. After this ammount
35 of days the TUF client won't accept this files.
36 """
37 EXPIRATION_DAYS = 90
38
39
40 def usage():
41     print "Usage:  %s repo key" % (sys.argv[0],)
42
43
44 def main():
45     if len(sys.argv) < 3:
46         usage()
47         return
48
49     repo_path = sys.argv[1]
50     key_path = sys.argv[2]
51     targets = Targets(repo_path, key_path)
52     targets.build()
53
54     print "%s/metadata.staged/(targets|snapshot).json[.gz] are ready" % \
55           (repo_path,)
56
57
58 class Targets(object):
59     """
60     Targets builder class
61     """
62
63     def __init__(self, repo_path, key_path):
64         """
65         Constructor
66
67         :param repo_path: path where the repo lives
68         :type repo_path: str
69         :param key_path: path where the private targets key lives
70         :type key_path: str
71         """
72         self._repo_path = repo_path
73         self._key = import_rsa_privatekey_from_file(key_path)
74
75     def build(self):
76         """
77         Generate snapshot.json[.gz] and targets.json[.gz]
78         """
79         self._repo = load_repository(self._repo_path)
80         self._load_targets()
81
82         self._repo.targets.load_signing_key(self._key)
83         self._repo.snapshot.load_signing_key(self._key)
84         self._repo.targets.compressions = ["gz"]
85         self._repo.snapshot.compressions = ["gz"]
86         self._repo.snapshot.expiration = (
87             datetime.datetime.now() +
88             datetime.timedelta(days=EXPIRATION_DAYS))
89         self._repo.targets.expiration = (
90             datetime.datetime.now() +
91             datetime.timedelta(days=EXPIRATION_DAYS))
92         self._repo.write_partial()
93
94     def _load_targets(self):
95         """
96         Load a list of targets
97         """
98         targets_path = os.path.join(self._repo_path, 'targets')
99         target_list = self._repo.get_filepaths_in_directory(
100             targets_path,
101             recursive_walk=True,
102             followlinks=True)
103
104         self._remove_obsolete_targets(target_list)
105
106         for target in target_list:
107             octal_file_permissions = oct(os.stat(target).st_mode)[3:]
108             custom_file_permissions = {
109                 'file_permissions': octal_file_permissions
110             }
111             self._repo.targets.add_target(target, custom_file_permissions)
112
113     def _remove_obsolete_targets(self, target_list):
114         """
115         Remove obsolete targets from TUF targets
116
117         :param target_list: list of targets on full path comming from TUF
118                             get_filepaths_in_directory
119         :type target_list: list(str)
120         """
121         targets_path = os.path.join(self._repo_path, 'targets')
122         relative_path_list = map(lambda t: t.split("/targets")[1], target_list)
123         removed_targets = (set(self._repo.targets.target_files.keys())
124                            - set(relative_path_list))
125
126         for target in removed_targets:
127             target_rel_path = target
128             if target[0] == '/':
129                 target_rel_path = target[1:]
130             target_path = os.path.join(targets_path, target_rel_path)
131             self._repo.targets.remove_target(target_path)
132
133
134 if __name__ == "__main__":
135     main()