Mercurial > hg
comparison hgext/chainsaw.py @ 51429:bc88aa7472de
chainsaw: new extension for dangerous operations
The first provided command is `chainsaw-update`, whose one and single job is
to make sure that it will pull, update and purge the target repository,
no matter what may be in the way (locks, notably), see docstring for rationale.
author | Georges Racinet <georges.racinet@octobus.net> |
---|---|
date | Sat, 26 Nov 2022 12:23:56 +0100 |
parents | |
children | fe68a2dc0bf2 |
comparison
equal
deleted
inserted
replaced
51428:def497c75351 | 51429:bc88aa7472de |
---|---|
1 # chainsaw.py | |
2 # | |
3 # Copyright 2022 Georges Racinet <georges.racinet@octobus.net> | |
4 # | |
5 # This software may be used and distributed according to the terms of the | |
6 # GNU General Public License version 2 or any later version. | |
7 """chainsaw is a collection of single-minded and dangerous tools. (EXPERIMENTAL) | |
8 | |
9 "Don't use a chainsaw to cut your food!" | |
10 | |
11 The chainsaw extension provides commands that are so much geared towards a | |
12 specific use case in a specific context or environment that they are totally | |
13 inappropriate and **really dangerous** in other contexts. | |
14 | |
15 The help text of each command explicitly summarizes its context of application | |
16 and the wanted end result. | |
17 | |
18 It is recommended to run these commands with the ``HGPLAIN`` environment | |
19 variable (see :hg:`help scripting`). | |
20 """ | |
21 | |
22 import shutil | |
23 | |
24 from mercurial.i18n import _ | |
25 from mercurial import ( | |
26 cmdutil, | |
27 commands, | |
28 error, | |
29 registrar, | |
30 ) | |
31 | |
32 cmdtable = {} | |
33 command = registrar.command(cmdtable) | |
34 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for | |
35 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should | |
36 # be specifying the version(s) of Mercurial they are tested with, or | |
37 # leave the attribute unspecified. | |
38 testedwith = b'ships-with-hg-core' | |
39 | |
40 | |
41 @command( | |
42 b'admin::chainsaw-update', | |
43 [ | |
44 ( | |
45 b'', | |
46 b'purge-unknown', | |
47 True, | |
48 _( | |
49 b'Remove unversioned files before update. Disabling this can ' | |
50 b'in some cases interfere with the update.' | |
51 b'See also :hg:`purge`.' | |
52 ), | |
53 ), | |
54 ( | |
55 b'', | |
56 b'purge-ignored', | |
57 True, | |
58 _( | |
59 b'Remove ignored files before update. Disable this for ' | |
60 b'instance to reuse previous compiler object files. ' | |
61 b'See also :hg:`purge`.' | |
62 ), | |
63 ), | |
64 ( | |
65 b'', | |
66 b'rev', | |
67 b'', | |
68 _(b'revision to update to'), | |
69 ), | |
70 ( | |
71 b'', | |
72 b'source', | |
73 b'', | |
74 _(b'repository to clone from'), | |
75 ), | |
76 ], | |
77 _(b'hg admin::chainsaw-update [OPTION] --rev REV --source SOURCE...'), | |
78 helpbasic=True, | |
79 ) | |
80 def update(ui, repo, **opts): | |
81 """pull and update to a given revision, no matter what, (EXPERIMENTAL) | |
82 | |
83 Context of application: *some* Continuous Integration (CI) systems, | |
84 packaging or deployment tools. | |
85 | |
86 Wanted end result: clean working directory updated at the given revision. | |
87 | |
88 chainsaw-update pulls from one source, then updates the working directory | |
89 to the given revision, overcoming anything that would stand in the way. | |
90 | |
91 By default, it will: | |
92 | |
93 - break locks if needed, leading to possible corruption if there | |
94 is a concurrent write access. | |
95 - perform recovery actions if needed | |
96 - revert any local modification. | |
97 - purge unknown and ignored files. | |
98 - go as far as to reclone if everything else failed (not implemented yet). | |
99 | |
100 DO NOT use it for anything else than performing a series | |
101 of unattended updates, with full exclusive repository access each time | |
102 and without any other local work than running build scripts. | |
103 In case the local repository is a share (see :hg:`help share`), exclusive | |
104 write access to the share source is also mandatory. | |
105 | |
106 It is recommended to run these commands with the ``HGPLAIN`` environment | |
107 variable (see :hg:`scripting`). | |
108 | |
109 Motivation: in Continuous Integration and Delivery systems (CI/CD), the | |
110 occasional remnant or bogus lock are common sources of waste of time (both | |
111 working time and calendar time). CI/CD scripts tend to grow with counter- | |
112 measures, often done in urgency. Also, whilst it is neat to keep | |
113 repositories from one job to the next (especially with large | |
114 repositories), an exceptional recloning is better than missing a release | |
115 deadline. | |
116 """ | |
117 rev = opts['rev'] | |
118 source = opts['source'] | |
119 if not rev: | |
120 raise error.InputError(_(b'specify a target revision with --rev')) | |
121 if not source: | |
122 raise error.InputError(_(b'specify a pull path with --source')) | |
123 ui.status(_(b'breaking locks, if any\n')) | |
124 repo.svfs.tryunlink(b'lock') | |
125 repo.vfs.tryunlink(b'wlock') | |
126 | |
127 ui.status(_(b'recovering after interrupted transaction, if any\n')) | |
128 repo.recover() | |
129 | |
130 ui.status(_(b'pulling from %s\n') % source) | |
131 overrides = {(b'ui', b'quiet'): True} | |
132 with ui.configoverride(overrides, b'chainsaw-update'): | |
133 pull = cmdutil.findcmd(b'pull', commands.table)[1][0] | |
134 pull(ui, repo, source, rev=[rev], remote_hidden=False) | |
135 | |
136 purge = cmdutil.findcmd(b'purge', commands.table)[1][0] | |
137 purge( | |
138 ui, | |
139 repo, | |
140 dirs=True, | |
141 all=opts.get('purge_ignored'), | |
142 files=opts.get('purge_unknown'), | |
143 confirm=False, | |
144 ) | |
145 | |
146 ui.status(_(b'updating to revision \'%s\'\n') % rev) | |
147 update = cmdutil.findcmd(b'update', commands.table)[1][0] | |
148 update(ui, repo, rev=rev, clean=True) | |
149 | |
150 ui.status( | |
151 _( | |
152 b'chainsaw-update to revision \'%s\' ' | |
153 b'for repository at \'%s\' done\n' | |
154 ) | |
155 % (rev, repo.root) | |
156 ) |