]>
Commit | Line | Data |
---|---|---|
c10aa1f0 | 1 | #!/usr/bin/env python3 |
81f86cb9 | 2 | |
83ffe9cd | 3 | # Copyright (C) 2020-2023 Free Software Foundation, Inc. |
c10aa1f0 ML |
4 | # |
5 | # This file is part of GCC. | |
6 | # | |
7 | # GCC is free software; you can redistribute it and/or modify it under | |
8 | # the terms of the GNU General Public License as published by the Free | |
9 | # Software Foundation; either version 3, or (at your option) any later | |
10 | # version. | |
11 | # | |
12 | # GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
13 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | # for more details. | |
16 | # | |
17 | # You should have received a copy of the GNU General Public License | |
18 | # along with GCC; see the file COPYING3. If not see | |
19 | # <http://www.gnu.org/licenses/>. */ | |
20 | ||
21 | import argparse | |
22 | import datetime | |
ed7278d9 | 23 | import logging |
c10aa1f0 ML |
24 | import os |
25 | ||
26 | from git import Repo | |
27 | ||
28 | from git_repository import parse_git_revisions | |
29 | ||
62b5b53e | 30 | current_timestamp = datetime.datetime.now().strftime('%Y%m%d\n') |
c10aa1f0 | 31 | |
5f6a43d6 | 32 | # Skip the following commits, they cannot be correctly processed |
e01874cd ML |
33 | IGNORED_COMMITS = ( |
34 | 'c2be82058fb40f3ae891c68d185ff53e07f14f45', | |
e4604715 | 35 | '04a040d907a83af54e0a98bdba5bfabc0ef4f700', |
0268c547 | 36 | '2e96b5f14e4025691b57d2301d71aa6092ed44bc', |
865e36a0 | 37 | '3ab5c8cd03d92bf4ec41e351820349d92fbc40c4', |
2175b397 | 38 | '86d8e0c0652ef5236a460b75c25e4f7093cc0651', |
f003fdff JJ |
39 | 'e4cba49413ca429dc82f6aa2e88129ecb3fdd943', |
40 | '1957bedf29a1b2cc231972aba680fe80199d5498') | |
5f6a43d6 | 41 | |
ed7278d9 ML |
42 | FORMAT = '%(asctime)s:%(levelname)s:%(name)s:%(message)s' |
43 | logging.basicConfig(level=logging.INFO, format=FORMAT, | |
44 | handlers=[ | |
45 | logging.FileHandler('/tmp/git_update_version.txt'), | |
46 | logging.StreamHandler() | |
47 | ]) | |
48 | ||
c10aa1f0 ML |
49 | |
50 | def read_timestamp(path): | |
bb07057a PMR |
51 | with open(path) as f: |
52 | return f.read() | |
c10aa1f0 ML |
53 | |
54 | ||
c8462662 | 55 | def prepend_to_changelog_files(repo, folder, git_commit, add_to_git): |
c10aa1f0 ML |
56 | if not git_commit.success: |
57 | for error in git_commit.errors: | |
ed7278d9 | 58 | logging.info(error) |
62b5b53e | 59 | raise AssertionError() |
4a5d072a | 60 | for entry, output in git_commit.to_changelog_entries(use_commit_ts=True): |
62b5b53e | 61 | full_path = os.path.join(folder, entry, 'ChangeLog') |
ed7278d9 | 62 | logging.info('writing to %s' % full_path) |
c10aa1f0 | 63 | if os.path.exists(full_path): |
bb07057a PMR |
64 | with open(full_path) as f: |
65 | content = f.read() | |
c10aa1f0 ML |
66 | else: |
67 | content = '' | |
68 | with open(full_path, 'w+') as f: | |
69 | f.write(output) | |
70 | if content: | |
71 | f.write('\n\n') | |
72 | f.write(content) | |
c8462662 ML |
73 | if add_to_git: |
74 | repo.git.add(full_path) | |
c10aa1f0 ML |
75 | |
76 | ||
c6264357 | 77 | active_refs = ['master', 'releases/gcc-10', |
f46ab321 | 78 | 'releases/gcc-11', 'releases/gcc-12', 'releases/gcc-13'] |
c10aa1f0 ML |
79 | |
80 | parser = argparse.ArgumentParser(description='Update DATESTAMP and generate ' | |
81 | 'ChangeLog entries') | |
82 | parser.add_argument('-g', '--git-path', default='.', | |
83 | help='Path to git repository') | |
c8462662 ML |
84 | parser.add_argument('-p', '--push', action='store_true', |
85 | help='Push updated active branches') | |
86 | parser.add_argument('-d', '--dry-mode', | |
87 | help='Generate patch for ChangeLog entries and do it' | |
88 | ' even if DATESTAMP is unchanged; folder argument' | |
89 | ' is expected') | |
be11812e ML |
90 | parser.add_argument('-c', '--current', action='store_true', |
91 | help='Modify current branch (--push argument is ignored)') | |
c10aa1f0 ML |
92 | args = parser.parse_args() |
93 | ||
94 | repo = Repo(args.git_path) | |
95 | origin = repo.remotes['origin'] | |
96 | ||
be11812e | 97 | |
5caadfbd | 98 | def update_current_branch(ref_name): |
be11812e ML |
99 | commit = repo.head.commit |
100 | commit_count = 1 | |
101 | while commit: | |
102 | if (commit.author.email == 'gccadmin@gcc.gnu.org' | |
103 | and commit.message.strip() == 'Daily bump.'): | |
104 | break | |
2c535665 ML |
105 | # We support merge commits but only with 2 parensts |
106 | assert len(commit.parents) <= 2 | |
107 | commit = commit.parents[-1] | |
be11812e ML |
108 | commit_count += 1 |
109 | ||
ed7278d9 | 110 | logging.info('%d revisions since last Daily bump' % commit_count) |
be11812e ML |
111 | datestamp_path = os.path.join(args.git_path, 'gcc/DATESTAMP') |
112 | if (read_timestamp(datestamp_path) != current_timestamp | |
113 | or args.dry_mode or args.current): | |
2c535665 ML |
114 | head = repo.head.commit |
115 | # if HEAD is a merge commit, start with second parent | |
116 | # (branched that is being merged into the current one) | |
117 | assert len(head.parents) <= 2 | |
118 | if len(head.parents) == 2: | |
119 | head = head.parents[1] | |
120 | commits = parse_git_revisions(args.git_path, '%s..%s' | |
5caadfbd | 121 | % (commit.hexsha, head.hexsha), ref_name) |
5f6a43d6 | 122 | commits = [c for c in commits if c.info.hexsha not in IGNORED_COMMITS] |
be11812e ML |
123 | for git_commit in reversed(commits): |
124 | prepend_to_changelog_files(repo, args.git_path, git_commit, | |
125 | not args.dry_mode) | |
126 | if args.dry_mode: | |
127 | diff = repo.git.diff('HEAD') | |
128 | patch = os.path.join(args.dry_mode, | |
129 | branch.name.split('/')[-1] + '.patch') | |
130 | with open(patch, 'w+') as f: | |
131 | f.write(diff) | |
ed7278d9 | 132 | logging.info('branch diff written to %s' % patch) |
be11812e | 133 | repo.git.checkout(force=True) |
c10aa1f0 | 134 | else: |
be11812e | 135 | # update timestamp |
ed7278d9 | 136 | logging.info('DATESTAMP will be changed:') |
be11812e ML |
137 | with open(datestamp_path, 'w+') as f: |
138 | f.write(current_timestamp) | |
139 | repo.git.add(datestamp_path) | |
140 | if not args.current: | |
c8462662 | 141 | repo.index.commit('Daily bump.') |
fce601fd | 142 | logging.info('commit is done') |
c8462662 | 143 | if args.push: |
ed7278d9 ML |
144 | try: |
145 | repo.git.push('origin', branch) | |
146 | logging.info('branch is pushed') | |
147 | except Exception: | |
148 | logging.exception('git push failed') | |
be11812e | 149 | else: |
ed7278d9 | 150 | logging.info('DATESTAMP unchanged') |
be11812e ML |
151 | |
152 | ||
153 | if args.current: | |
ed7278d9 | 154 | logging.info('=== Working on the current branch ===') |
be11812e ML |
155 | update_current_branch() |
156 | else: | |
157 | for ref in origin.refs: | |
158 | assert ref.name.startswith('origin/') | |
159 | name = ref.name[len('origin/'):] | |
160 | if name in active_refs: | |
161 | if name in repo.branches: | |
162 | branch = repo.branches[name] | |
163 | else: | |
164 | branch = repo.create_head(name, ref).set_tracking_branch(ref) | |
ed7278d9 | 165 | logging.info('=== Working on: %s ===' % branch) |
be11812e | 166 | branch.checkout() |
9b4bdaf7 | 167 | origin.pull(rebase=True) |
ed7278d9 | 168 | logging.info('branch pulled and checked out') |
5caadfbd | 169 | update_current_branch(name) |
be11812e | 170 | assert not repo.index.diff(None) |
ed7278d9 ML |
171 | logging.info('branch is done') |
172 | logging.info('') |