summaryrefslogtreecommitdiffstats
path: root/scripts/oe-git-archive
diff options
context:
space:
mode:
authorRichard Purdie <richard.purdie@linuxfoundation.org>2019-02-17 22:12:40 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2019-02-18 10:33:13 +0000
commit18880a9f1cbc94b8c963f636e9756a1020fc5575 (patch)
treee2d80d3e29823ba6ea40c877539d12761f202d78 /scripts/oe-git-archive
parentc71dc290b0b9e634f6f2e9475928628137e8e565 (diff)
downloadpoky-18880a9f1cbc94b8c963f636e9756a1020fc5575.tar.gz
scripts/oe-git-archive: Separate out functionality to library function
This turns the core of the script into a library function. Ultimately this will let us call that code with custom 'keywords' rather than relying on the data parsed from bitbake metadata which can't be used when archiving historical results. (From OE-Core rev: 4820ca2b0850e29b04a4fd5659a6e9837d6714d0) Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts/oe-git-archive')
-rwxr-xr-xscripts/oe-git-archive166
1 files changed, 6 insertions, 160 deletions
diff --git a/scripts/oe-git-archive b/scripts/oe-git-archive
index 913291a99c..ab1c2b9ad4 100755
--- a/scripts/oe-git-archive
+++ b/scripts/oe-git-archive
@@ -14,16 +14,10 @@
14# more details. 14# more details.
15# 15#
16import argparse 16import argparse
17import glob
18import json
19import logging 17import logging
20import math
21import os 18import os
22import re 19import re
23import sys 20import sys
24from collections import namedtuple, OrderedDict
25from datetime import datetime, timedelta, tzinfo
26from operator import attrgetter
27 21
28# Import oe and bitbake libs 22# Import oe and bitbake libs
29scripts_path = os.path.dirname(os.path.realpath(__file__)) 23scripts_path = os.path.dirname(os.path.realpath(__file__))
@@ -34,128 +28,13 @@ scriptpath.add_oe_lib_path()
34 28
35from oeqa.utils.git import GitRepo, GitError 29from oeqa.utils.git import GitRepo, GitError
36from oeqa.utils.metadata import metadata_from_bb 30from oeqa.utils.metadata import metadata_from_bb
37 31import oeqa.utils.gitarchive as gitarchive
38 32
39# Setup logging 33# Setup logging
40logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") 34logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
41log = logging.getLogger() 35log = logging.getLogger()
42 36
43 37
44class ArchiveError(Exception):
45 """Internal error handling of this script"""
46
47
48def format_str(string, fields):
49 """Format string using the given fields (dict)"""
50 try:
51 return string.format(**fields)
52 except KeyError as err:
53 raise ArchiveError("Unable to expand string '{}': unknown field {} "
54 "(valid fields are: {})".format(
55 string, err, ', '.join(sorted(fields.keys()))))
56
57
58def init_git_repo(path, no_create, bare):
59 """Initialize local Git repository"""
60 path = os.path.abspath(path)
61 if os.path.isfile(path):
62 raise ArchiveError("Invalid Git repo at {}: path exists but is not a "
63 "directory".format(path))
64 if not os.path.isdir(path) or not os.listdir(path):
65 if no_create:
66 raise ArchiveError("No git repo at {}, refusing to create "
67 "one".format(path))
68 if not os.path.isdir(path):
69 try:
70 os.mkdir(path)
71 except (FileNotFoundError, PermissionError) as err:
72 raise ArchiveError("Failed to mkdir {}: {}".format(path, err))
73 if not os.listdir(path):
74 log.info("Initializing a new Git repo at %s", path)
75 repo = GitRepo.init(path, bare)
76 try:
77 repo = GitRepo(path, is_topdir=True)
78 except GitError:
79 raise ArchiveError("Non-empty directory that is not a Git repository "
80 "at {}\nPlease specify an existing Git repository, "
81 "an empty directory or a non-existing directory "
82 "path.".format(path))
83 return repo
84
85
86def git_commit_data(repo, data_dir, branch, message, exclude, notes):
87 """Commit data into a Git repository"""
88 log.info("Committing data into to branch %s", branch)
89 tmp_index = os.path.join(repo.git_dir, 'index.oe-git-archive')
90 try:
91 # Create new tree object from the data
92 env_update = {'GIT_INDEX_FILE': tmp_index,
93 'GIT_WORK_TREE': os.path.abspath(data_dir)}
94 repo.run_cmd('add .', env_update)
95
96 # Remove files that are excluded
97 if exclude:
98 repo.run_cmd(['rm', '--cached'] + [f for f in exclude], env_update)
99
100 tree = repo.run_cmd('write-tree', env_update)
101
102 # Create new commit object from the tree
103 parent = repo.rev_parse(branch)
104 git_cmd = ['commit-tree', tree, '-m', message]
105 if parent:
106 git_cmd += ['-p', parent]
107 commit = repo.run_cmd(git_cmd, env_update)
108
109 # Create git notes
110 for ref, filename in notes:
111 ref = ref.format(branch_name=branch)
112 repo.run_cmd(['notes', '--ref', ref, 'add',
113 '-F', os.path.abspath(filename), commit])
114
115 # Update branch head
116 git_cmd = ['update-ref', 'refs/heads/' + branch, commit]
117 if parent:
118 git_cmd.append(parent)
119 repo.run_cmd(git_cmd)
120
121 # Update current HEAD, if we're on branch 'branch'
122 if not repo.bare and repo.get_current_branch() == branch:
123 log.info("Updating %s HEAD to latest commit", repo.top_dir)
124 repo.run_cmd('reset --hard')
125
126 return commit
127 finally:
128 if os.path.exists(tmp_index):
129 os.unlink(tmp_index)
130
131
132def expand_tag_strings(repo, name_pattern, msg_subj_pattern, msg_body_pattern,
133 keywords):
134 """Generate tag name and message, with support for running id number"""
135 keyws = keywords.copy()
136 # Tag number is handled specially: if not defined, we autoincrement it
137 if 'tag_number' not in keyws:
138 # Fill in all other fields than 'tag_number'
139 keyws['tag_number'] = '{tag_number}'
140 tag_re = format_str(name_pattern, keyws)
141 # Replace parentheses for proper regex matching
142 tag_re = tag_re.replace('(', '\(').replace(')', '\)') + '$'
143 # Inject regex group pattern for 'tag_number'
144 tag_re = tag_re.format(tag_number='(?P<tag_number>[0-9]{1,5})')
145
146 keyws['tag_number'] = 0
147 for existing_tag in repo.run_cmd('tag').splitlines():
148 match = re.match(tag_re, existing_tag)
149
150 if match and int(match.group('tag_number')) >= keyws['tag_number']:
151 keyws['tag_number'] = int(match.group('tag_number')) + 1
152
153 tag_name = format_str(name_pattern, keyws)
154 msg_subj= format_str(msg_subj_pattern.strip(), keyws)
155 msg_body = format_str(msg_body_pattern, keyws)
156 return tag_name, msg_subj + '\n\n' + msg_body
157
158
159def parse_args(argv): 38def parse_args(argv):
160 """Parse command line arguments""" 39 """Parse command line arguments"""
161 parser = argparse.ArgumentParser( 40 parser = argparse.ArgumentParser(
@@ -217,17 +96,11 @@ def get_nested(d, list_of_keys):
217 return "" 96 return ""
218 97
219def main(argv=None): 98def main(argv=None):
220 """Script entry point"""
221 args = parse_args(argv) 99 args = parse_args(argv)
222 if args.debug: 100 if args.debug:
223 log.setLevel(logging.DEBUG) 101 log.setLevel(logging.DEBUG)
224 102
225 try: 103 try:
226 if not os.path.isdir(args.data_dir):
227 raise ArchiveError("Not a directory: {}".format(args.data_dir))
228
229 data_repo = init_git_repo(args.git_dir, args.no_create, args.bare)
230
231 # Get keywords to be used in tag and branch names and messages 104 # Get keywords to be used in tag and branch names and messages
232 metadata = metadata_from_bb() 105 metadata = metadata_from_bb()
233 keywords = {'hostname': get_nested(metadata, ['hostname']), 106 keywords = {'hostname': get_nested(metadata, ['hostname']),
@@ -236,39 +109,12 @@ def main(argv=None):
236 'commit_count': get_nested(metadata, ['layers', 'meta', 'commit_count']), 109 'commit_count': get_nested(metadata, ['layers', 'meta', 'commit_count']),
237 'machine': get_nested(metadata, ['config', 'MACHINE'])} 110 'machine': get_nested(metadata, ['config', 'MACHINE'])}
238 111
239 # Expand strings early in order to avoid getting into inconsistent 112 gitarchive.gitarchive(args.data_dir, args.git_dir, args.no_create, args.bare,
240 # state (e.g. no tag even if data was committed) 113 args.commit_msg_subject.strip(), args.commit_msg_body, args.branch_name,
241 commit_msg = format_str(args.commit_msg_subject.strip(), keywords) 114 args.no_tag, args.tag_name, args.tag_msg_subject, args.tag_msg_body,
242 commit_msg += '\n\n' + format_str(args.commit_msg_body, keywords) 115 args.exclude, args.notes, args.push, keywords, log)
243 branch_name = format_str(args.branch_name, keywords)
244 tag_name = None
245 if not args.no_tag and args.tag_name:
246 tag_name, tag_msg = expand_tag_strings(data_repo, args.tag_name,
247 args.tag_msg_subject,
248 args.tag_msg_body, keywords)
249
250 # Commit data
251 commit = git_commit_data(data_repo, args.data_dir, branch_name,
252 commit_msg, args.exclude, args.notes)
253
254 # Create tag
255 if tag_name:
256 log.info("Creating tag %s", tag_name)
257 data_repo.run_cmd(['tag', '-a', '-m', tag_msg, tag_name, commit])
258
259 # Push data to remote
260 if args.push:
261 cmd = ['push', '--tags']
262 # If no remote is given we push with the default settings from
263 # gitconfig
264 if args.push is not True:
265 notes_refs = ['refs/notes/' + ref.format(branch_name=branch_name)
266 for ref, _ in args.notes]
267 cmd.extend([args.push, branch_name] + notes_refs)
268 log.info("Pushing data to remote")
269 data_repo.run_cmd(cmd)
270 116
271 except ArchiveError as err: 117 except gitarchive.ArchiveError as err:
272 log.error(str(err)) 118 log.error(str(err))
273 return 1 119 return 1
274 120