##// END OF EJS Templates
chainsaw-update: log actual locks breaking...
Georges Racinet -
r52324:fe68a2dc default
parent child Browse files
Show More
@@ -1,156 +1,157 b''
1 1 # chainsaw.py
2 2 #
3 3 # Copyright 2022 Georges Racinet <georges.racinet@octobus.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 """chainsaw is a collection of single-minded and dangerous tools. (EXPERIMENTAL)
8 8
9 9 "Don't use a chainsaw to cut your food!"
10 10
11 11 The chainsaw extension provides commands that are so much geared towards a
12 12 specific use case in a specific context or environment that they are totally
13 13 inappropriate and **really dangerous** in other contexts.
14 14
15 15 The help text of each command explicitly summarizes its context of application
16 16 and the wanted end result.
17 17
18 18 It is recommended to run these commands with the ``HGPLAIN`` environment
19 19 variable (see :hg:`help scripting`).
20 20 """
21 21
22 22 import shutil
23 23
24 24 from mercurial.i18n import _
25 25 from mercurial import (
26 26 cmdutil,
27 27 commands,
28 28 error,
29 29 registrar,
30 30 )
31 31
32 32 cmdtable = {}
33 33 command = registrar.command(cmdtable)
34 34 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
35 35 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
36 36 # be specifying the version(s) of Mercurial they are tested with, or
37 37 # leave the attribute unspecified.
38 38 testedwith = b'ships-with-hg-core'
39 39
40 40
41 41 @command(
42 42 b'admin::chainsaw-update',
43 43 [
44 44 (
45 45 b'',
46 46 b'purge-unknown',
47 47 True,
48 48 _(
49 49 b'Remove unversioned files before update. Disabling this can '
50 50 b'in some cases interfere with the update.'
51 51 b'See also :hg:`purge`.'
52 52 ),
53 53 ),
54 54 (
55 55 b'',
56 56 b'purge-ignored',
57 57 True,
58 58 _(
59 59 b'Remove ignored files before update. Disable this for '
60 60 b'instance to reuse previous compiler object files. '
61 61 b'See also :hg:`purge`.'
62 62 ),
63 63 ),
64 64 (
65 65 b'',
66 66 b'rev',
67 67 b'',
68 68 _(b'revision to update to'),
69 69 ),
70 70 (
71 71 b'',
72 72 b'source',
73 73 b'',
74 74 _(b'repository to clone from'),
75 75 ),
76 76 ],
77 77 _(b'hg admin::chainsaw-update [OPTION] --rev REV --source SOURCE...'),
78 78 helpbasic=True,
79 79 )
80 80 def update(ui, repo, **opts):
81 81 """pull and update to a given revision, no matter what, (EXPERIMENTAL)
82 82
83 83 Context of application: *some* Continuous Integration (CI) systems,
84 84 packaging or deployment tools.
85 85
86 86 Wanted end result: clean working directory updated at the given revision.
87 87
88 88 chainsaw-update pulls from one source, then updates the working directory
89 89 to the given revision, overcoming anything that would stand in the way.
90 90
91 91 By default, it will:
92 92
93 93 - break locks if needed, leading to possible corruption if there
94 94 is a concurrent write access.
95 95 - perform recovery actions if needed
96 96 - revert any local modification.
97 97 - purge unknown and ignored files.
98 98 - go as far as to reclone if everything else failed (not implemented yet).
99 99
100 100 DO NOT use it for anything else than performing a series
101 101 of unattended updates, with full exclusive repository access each time
102 102 and without any other local work than running build scripts.
103 103 In case the local repository is a share (see :hg:`help share`), exclusive
104 104 write access to the share source is also mandatory.
105 105
106 106 It is recommended to run these commands with the ``HGPLAIN`` environment
107 107 variable (see :hg:`scripting`).
108 108
109 109 Motivation: in Continuous Integration and Delivery systems (CI/CD), the
110 110 occasional remnant or bogus lock are common sources of waste of time (both
111 111 working time and calendar time). CI/CD scripts tend to grow with counter-
112 112 measures, often done in urgency. Also, whilst it is neat to keep
113 113 repositories from one job to the next (especially with large
114 114 repositories), an exceptional recloning is better than missing a release
115 115 deadline.
116 116 """
117 117 rev = opts['rev']
118 118 source = opts['source']
119 119 if not rev:
120 120 raise error.InputError(_(b'specify a target revision with --rev'))
121 121 if not source:
122 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')
123 if repo.svfs.tryunlink(b'lock'):
124 ui.status(_(b'had to break store lock\n'))
125 if repo.vfs.tryunlink(b'wlock'):
126 ui.status(_(b'had to break working copy lock\n'))
126 127
127 128 ui.status(_(b'recovering after interrupted transaction, if any\n'))
128 129 repo.recover()
129 130
130 131 ui.status(_(b'pulling from %s\n') % source)
131 132 overrides = {(b'ui', b'quiet'): True}
132 133 with ui.configoverride(overrides, b'chainsaw-update'):
133 134 pull = cmdutil.findcmd(b'pull', commands.table)[1][0]
134 135 pull(ui, repo, source, rev=[rev], remote_hidden=False)
135 136
136 137 purge = cmdutil.findcmd(b'purge', commands.table)[1][0]
137 138 purge(
138 139 ui,
139 140 repo,
140 141 dirs=True,
141 142 all=opts.get('purge_ignored'),
142 143 files=opts.get('purge_unknown'),
143 144 confirm=False,
144 145 )
145 146
146 147 ui.status(_(b'updating to revision \'%s\'\n') % rev)
147 148 update = cmdutil.findcmd(b'update', commands.table)[1][0]
148 149 update(ui, repo, rev=rev, clean=True)
149 150
150 151 ui.status(
151 152 _(
152 153 b'chainsaw-update to revision \'%s\' '
153 154 b'for repository at \'%s\' done\n'
154 155 )
155 156 % (rev, repo.root)
156 157 )
@@ -1,105 +1,111 b''
1 1 ============================================
2 2 Tests for the admin::chainsaw-update command
3 3 ============================================
4 4
5 5 setup
6 6 =====
7 7
8 8 $ cat >> $HGRCPATH << EOF
9 9 > [extensions]
10 10 > chainsaw=
11 11 > EOF
12 12
13 13 $ hg init src
14 14 $ cd src
15 15 $ echo 1 > foo
16 16 $ hg ci -Am1
17 17 adding foo
18 18 $ cd ..
19 19
20 20 Actual tests
21 21 ============
22 22
23 23 Simple invocation
24 24 -----------------
25 25
26 26 $ hg init repo
27 27 $ cd repo
28 28 $ hg admin::chainsaw-update --rev default --source ../src
29 breaking locks, if any
30 29 recovering after interrupted transaction, if any
31 30 no interrupted transaction available
32 31 pulling from ../src
33 32 updating to revision 'default'
34 33 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
35 34 chainsaw-update to revision 'default' for repository at '$TESTTMP/repo' done
36 35
37 36 $ cat foo
38 37 1
39 38
40 39 Test lock breacking capabilities
41 40 --------------------------------
42 41
43 42 Demonstrate lock-breaking capabilities with locks that regular Mercurial
44 43 operation would not break, because the hostnames registered in locks differ
45 44 from the current hostname (happens a lot with succesive containers):
46 45
47 46 $ ln -s invalid.host.test/effffffc:171814 .hg/store/lock
48 47 $ ln -s invalid.host.test/effffffc:171814 .hg/wlock
49 48 $ hg debuglock
50 49 lock: (.*?), process 171814, host invalid.host.test/effffffc \((\d+)s\) (re)
51 50 wlock: (.*?), process 171814, host invalid.host.test/effffffc \((\d+)s\) (re)
52 51 [2]
53 52
54 $ hg admin::chainsaw-update --no-purge-ignored --rev default --source ../src -q
53 $ hg admin::chainsaw-update --no-purge-ignored --rev default --source ../src
54 had to break store lock
55 had to break working copy lock
56 recovering after interrupted transaction, if any
55 57 no interrupted transaction available
58 pulling from ../src
59 updating to revision 'default'
60 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
61 chainsaw-update to revision 'default' for repository at '$TESTTMP/repo' done
56 62
57 63 Test file purging capabilities
58 64 ------------------------------
59 65
60 66 Let's also add local modifications (tracked and untracked) to demonstrate the
61 67 purging.
62 68
63 69 $ echo untracked > bar
64 70 $ echo modified > foo
65 71 $ hg status -A
66 72 M foo
67 73 ? bar
68 74
69 75 $ echo 2 > ../src/foo
70 76 $ hg -R ../src commit -m2
71 77 $ hg admin::chainsaw-update --rev default --source ../src -q
72 78 no interrupted transaction available
73 79 $ hg status -A
74 80 C foo
75 81 $ cat foo
76 82 2
77 83
78 84 Now behaviour with respect to ignored files: they are not purged if
79 85 the --no-purge-ignored flag is passed, but they are purged by default
80 86
81 87 $ echo bar > .hgignore
82 88 $ hg ci -Aqm hgignore
83 89 $ echo ignored > bar
84 90 $ hg status --all
85 91 I bar
86 92 C .hgignore
87 93 C foo
88 94
89 95 $ hg admin::chainsaw-update --no-purge-ignored --rev default --source ../src -q
90 96 no interrupted transaction available
91 97 $ hg status --all
92 98 I bar
93 99 C .hgignore
94 100 C foo
95 101 $ cat bar
96 102 ignored
97 103
98 104 $ hg admin::chainsaw-update --rev default --source ../src -q
99 105 no interrupted transaction available
100 106 $ hg status --all
101 107 C .hgignore
102 108 C foo
103 109 $ test -f bar
104 110 [1]
105 111
General Comments 0
You need to be logged in to leave comments. Login now