Show More
@@ -0,0 +1,156 b'' | |||||
|
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 | ) |
@@ -0,0 +1,105 b'' | |||||
|
1 | ============================================ | |||
|
2 | Tests for the admin::chainsaw-update command | |||
|
3 | ============================================ | |||
|
4 | ||||
|
5 | setup | |||
|
6 | ===== | |||
|
7 | ||||
|
8 | $ cat >> $HGRCPATH << EOF | |||
|
9 | > [extensions] | |||
|
10 | > chainsaw= | |||
|
11 | > EOF | |||
|
12 | ||||
|
13 | $ hg init src | |||
|
14 | $ cd src | |||
|
15 | $ echo 1 > foo | |||
|
16 | $ hg ci -Am1 | |||
|
17 | adding foo | |||
|
18 | $ cd .. | |||
|
19 | ||||
|
20 | Actual tests | |||
|
21 | ============ | |||
|
22 | ||||
|
23 | Simple invocation | |||
|
24 | ----------------- | |||
|
25 | ||||
|
26 | $ hg init repo | |||
|
27 | $ cd repo | |||
|
28 | $ hg admin::chainsaw-update --rev default --source ../src | |||
|
29 | breaking locks, if any | |||
|
30 | recovering after interrupted transaction, if any | |||
|
31 | no interrupted transaction available | |||
|
32 | pulling from ../src | |||
|
33 | updating to revision 'default' | |||
|
34 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | |||
|
35 | chainsaw-update to revision 'default' for repository at '$TESTTMP/repo' done | |||
|
36 | ||||
|
37 | $ cat foo | |||
|
38 | 1 | |||
|
39 | ||||
|
40 | Test lock breacking capabilities | |||
|
41 | -------------------------------- | |||
|
42 | ||||
|
43 | Demonstrate lock-breaking capabilities with locks that regular Mercurial | |||
|
44 | operation would not break, because the hostnames registered in locks differ | |||
|
45 | from the current hostname (happens a lot with succesive containers): | |||
|
46 | ||||
|
47 | $ ln -s invalid.host.test/effffffc:171814 .hg/store/lock | |||
|
48 | $ ln -s invalid.host.test/effffffc:171814 .hg/wlock | |||
|
49 | $ hg debuglock | |||
|
50 | lock: (.*?), process 171814, host invalid.host.test/effffffc \((\d+)s\) (re) | |||
|
51 | wlock: (.*?), process 171814, host invalid.host.test/effffffc \((\d+)s\) (re) | |||
|
52 | [2] | |||
|
53 | ||||
|
54 | $ hg admin::chainsaw-update --no-purge-ignored --rev default --source ../src -q | |||
|
55 | no interrupted transaction available | |||
|
56 | ||||
|
57 | Test file purging capabilities | |||
|
58 | ------------------------------ | |||
|
59 | ||||
|
60 | Let's also add local modifications (tracked and untracked) to demonstrate the | |||
|
61 | purging. | |||
|
62 | ||||
|
63 | $ echo untracked > bar | |||
|
64 | $ echo modified > foo | |||
|
65 | $ hg status -A | |||
|
66 | M foo | |||
|
67 | ? bar | |||
|
68 | ||||
|
69 | $ echo 2 > ../src/foo | |||
|
70 | $ hg -R ../src commit -m2 | |||
|
71 | $ hg admin::chainsaw-update --rev default --source ../src -q | |||
|
72 | no interrupted transaction available | |||
|
73 | $ hg status -A | |||
|
74 | C foo | |||
|
75 | $ cat foo | |||
|
76 | 2 | |||
|
77 | ||||
|
78 | Now behaviour with respect to ignored files: they are not purged if | |||
|
79 | the --no-purge-ignored flag is passed, but they are purged by default | |||
|
80 | ||||
|
81 | $ echo bar > .hgignore | |||
|
82 | $ hg ci -Aqm hgignore | |||
|
83 | $ echo ignored > bar | |||
|
84 | $ hg status --all | |||
|
85 | I bar | |||
|
86 | C .hgignore | |||
|
87 | C foo | |||
|
88 | ||||
|
89 | $ hg admin::chainsaw-update --no-purge-ignored --rev default --source ../src -q | |||
|
90 | no interrupted transaction available | |||
|
91 | $ hg status --all | |||
|
92 | I bar | |||
|
93 | C .hgignore | |||
|
94 | C foo | |||
|
95 | $ cat bar | |||
|
96 | ignored | |||
|
97 | ||||
|
98 | $ hg admin::chainsaw-update --rev default --source ../src -q | |||
|
99 | no interrupted transaction available | |||
|
100 | $ hg status --all | |||
|
101 | C .hgignore | |||
|
102 | C foo | |||
|
103 | $ test -f bar | |||
|
104 | [1] | |||
|
105 |
General Comments 0
You need to be logged in to leave comments.
Login now