Commit | Line | Data |
---|---|---|
545fc9df TO |
1 | #!/usr/bin/env php |
2 | <?php | |
3 | ||
4 | // Update the data-files within this repo to reflect a new version number. | |
5 | // Example usage: | |
6 | // git checkout origin/master -b master-4.7.29 | |
7 | // ./tools/bin/scripts/set-version.php 4.7.29 --commit | |
8 | // git commit -m "Update to version 4.7.29" | |
9 | // git push origin master | |
10 | ||
11 | /* *********************************************************************** */ | |
12 | /* Boot */ | |
5c5ed6db | 13 | if (!(php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0))) { |
a9f3bf65 SL |
14 | header("HTTP/1.0 404 Not Found"); |
15 | return; | |
16 | } | |
545fc9df TO |
17 | $civicrm_root = dirname(dirname(dirname(__DIR__))); |
18 | chdir($civicrm_root); | |
19 | ||
20 | /* *********************************************************************** */ | |
21 | /* Parse inputs -- $oldVersion, $newVersion, $doCommit */ | |
22 | ||
23 | $oldVersion = (string) simplexml_load_file("xml/version.xml")->version_no; | |
24 | if (!isVersionValid($oldVersion)) { | |
25 | fatal("failed to read old version from \"xml/version.xml\"\n"); | |
26 | } | |
27 | ||
28a98d7e EM |
28 | /** |
29 | * @var string $newVersion */ | |
30 | /** | |
31 | * @var bool $doCommit */ | |
32 | /** | |
33 | * @var bool $doSql */ | |
b43bfb44 TO |
34 | extract(parseArgs($argv)); |
35 | ||
545fc9df TO |
36 | if (!isVersionValid($newVersion)) { |
37 | fatal("failed to read new version\n"); | |
38 | } | |
39 | ||
545fc9df TO |
40 | /* *********************************************************************** */ |
41 | /* Main */ | |
42 | ||
337dc87a TO |
43 | echo "Changing version from $oldVersion to $newVersion...\n"; |
44 | ||
45 | $verName = makeVerName($newVersion); | |
3bef53f6 | 46 | $phpFile = initFile("CRM/Upgrade/Incremental/php/{$verName}.php", function () use ($verName, $newVersion) { |
337dc87a TO |
47 | ob_start(); |
48 | global $camelNumber; | |
3bef53f6 | 49 | global $versionNumber; |
337dc87a | 50 | $camelNumber = $verName; |
3bef53f6 | 51 | $versionNumber = $newVersion; |
337dc87a | 52 | require 'CRM/Upgrade/Incremental/php/Template.php'; |
3bef53f6 | 53 | unset($camelNumber, $versionNumber); |
337dc87a TO |
54 | return ob_get_clean(); |
55 | }); | |
56 | ||
bd9556c5 TO |
57 | // It is typical for `*.alpha` to need SQL file -- and for `*.beta1` and `*.0` to NOT need a SQL file. |
58 | if ($doSql === TRUE || ($doSql === 'auto' && preg_match(';alpha;', $newVersion))) { | |
59 | $sqlFile = initFile("CRM/Upgrade/Incremental/sql/{$newVersion}.mysql.tpl", function () use ($newVersion) { | |
60 | return "{* file to handle db changes in $newVersion during upgrade *}\n"; | |
61 | }); | |
62 | } | |
545fc9df TO |
63 | |
64 | updateFile("xml/version.xml", function ($content) use ($newVersion, $oldVersion) { | |
65 | return str_replace($oldVersion, $newVersion, $content); | |
66 | }); | |
67 | ||
653b713e C |
68 | if (file_exists("civicrm-version.php")) { |
69 | updateFile("civicrm-version.php", function ($content) use ($newVersion, $oldVersion) { | |
70 | return str_replace($oldVersion, $newVersion, $content); | |
71 | }); | |
72 | } | |
4d4f529c | 73 | |
545fc9df TO |
74 | updateFile("sql/civicrm_generated.mysql", function ($content) use ($newVersion, $oldVersion) { |
75 | return str_replace($oldVersion, $newVersion, $content); | |
76 | }); | |
77 | ||
d071a643 SL |
78 | updateFile("sql/test_data_second_domain.mysql", function ($content) use ($newVersion, $oldVersion) { |
79 | return str_replace($oldVersion, $newVersion, $content); | |
80 | }); | |
81 | ||
a9b847ea TO |
82 | $infoXmls = findCoreInfoXml(); |
83 | foreach ($infoXmls as $infoXml) { | |
68122019 TO |
84 | updateXmlFile($infoXml, function (DOMDocument $dom) use ($newVersion) { |
85 | foreach ($dom->getElementsByTagName('version') as $tag) { | |
28a98d7e | 86 | /* @var \DOMNode $tag */ |
68122019 TO |
87 | $tag->textContent = $newVersion; |
88 | } | |
89 | }); | |
90 | } | |
91 | ||
545fc9df | 92 | if ($doCommit) { |
bd9556c5 | 93 | $files = array_filter( |
a9b847ea | 94 | array_merge(['xml/version.xml', 'sql/civicrm_generated.mysql', 'sql/test_data_second_domain.mysql', $phpFile, @$sqlFile], $infoXmls), |
bd9556c5 TO |
95 | 'file_exists' |
96 | ); | |
97 | $filesEsc = implode(' ', array_map('escapeshellarg', $files)); | |
98 | passthru("git add $filesEsc"); | |
99 | passthru("git commit $filesEsc -m " . escapeshellarg("Set version to $newVersion")); | |
545fc9df TO |
100 | } |
101 | ||
102 | /* *********************************************************************** */ | |
103 | /* Helper functions */ | |
104 | ||
337dc87a TO |
105 | /** |
106 | * Update the content of a file. | |
107 | * | |
108 | * @param string $file | |
109 | * @param callable $callback | |
110 | * Function(string $originalContent) => string $newContent. | |
111 | */ | |
545fc9df TO |
112 | function updateFile($file, $callback) { |
113 | if (!file_exists($file)) { | |
114 | die("File does not exist: $file\n"); | |
115 | } | |
116 | echo "Update \"$file\"\n"; | |
117 | $content = file_get_contents($file); | |
118 | $content = $callback($content); | |
119 | file_put_contents($file, $content); | |
120 | } | |
121 | ||
68122019 TO |
122 | /** |
123 | * Update the content of an XML file | |
124 | * | |
125 | * @param string $file | |
126 | * @param callable $callback | |
127 | * Function(DOMDocument $dom) | |
128 | */ | |
129 | function updateXmlFile($file, $callback) { | |
130 | updateFile($file, function ($content) use ($callback) { | |
131 | $dom = new DomDocument(); | |
132 | $dom->loadXML($content); | |
133 | $dom->preserveWhiteSpace = FALSE; | |
134 | $dom->formatOutput = TRUE; | |
135 | $callback($dom); | |
136 | return $dom->saveXML(); | |
137 | }); | |
138 | } | |
139 | ||
337dc87a TO |
140 | /** |
141 | * Initialize a file (if it doesn't already exist). | |
142 | * @param string $file | |
143 | * @param callable $callback | |
144 | * Function() => string $newContent. | |
145 | */ | |
146 | function initFile($file, $callback) { | |
147 | if (file_exists($file)) { | |
148 | echo "File \"$file\" already exists.\n"; | |
149 | } | |
150 | else { | |
151 | echo "Initialize \"$file\"\n"; | |
152 | $content = $callback(); | |
153 | file_put_contents($file, $content); | |
154 | } | |
155 | return $file; | |
156 | } | |
157 | ||
158 | /** | |
159 | * Render a pretty string for a major/minor version number. | |
160 | * | |
161 | * @param string $version | |
162 | * Ex: '5.10.alpha1' | |
163 | * @return string | |
164 | * Ex: 'FiveTen'. | |
165 | */ | |
166 | function makeVerName($version) { | |
b43bfb44 | 167 | [$a, $b] = explode('.', $version); |
337dc87a TO |
168 | require_once 'CRM/Utils/EnglishNumber.php'; |
169 | return CRM_Utils_EnglishNumber::toCamelCase($a) . CRM_Utils_EnglishNumber::toCamelCase($b); | |
170 | } | |
171 | ||
545fc9df TO |
172 | function isVersionValid($v) { |
173 | return $v && preg_match('/^[0-9a-z\.\-]+$/', $v); | |
174 | } | |
175 | ||
176 | /** | |
177 | * @param $error | |
178 | */ | |
179 | function fatal($error) { | |
180 | echo $error; | |
bd9556c5 TO |
181 | echo "usage: set-version.php <new-version> [--sql|--no-sql] [--commit|--no-commit]\n"; |
182 | echo " --sql A placeholder *.sql file will be created.\n"; | |
183 | echo " --no-sql A placeholder *.sql file will not be created.\n"; | |
184 | echo " --commit Any changes will be committed automatically the current git branch.\n"; | |
185 | echo " --no-commit Any changes will be left uncommitted.\n"; | |
186 | echo "\n"; | |
187 | echo "If the SQL style is not specified, it will decide automatically. (Alpha versions get SQL files.)\n"; | |
188 | echo "\n"; | |
189 | echo "You MUST indicate whether to commit.\n"; | |
545fc9df TO |
190 | exit(1); |
191 | } | |
b43bfb44 TO |
192 | |
193 | /** | |
28a98d7e | 194 | * @param array $argv |
b43bfb44 TO |
195 | * Ex: ['myscript.php', '--no-commit', '5.6.7'] |
196 | * @return array | |
28a98d7e | 197 | * Ex: ['scriptFile' => 'myscript.php', 'doCommit' => FALSE, 'newVersion' => '5.6.7'] |
b43bfb44 TO |
198 | */ |
199 | function parseArgs($argv) { | |
200 | $parsed = []; | |
bd9556c5 | 201 | $parsed['doSql'] = 'auto'; |
b43bfb44 TO |
202 | $positions = ['scriptFile', 'newVersion']; |
203 | $positional = []; | |
204 | ||
205 | foreach ($argv as $arg) { | |
206 | switch ($arg) { | |
207 | case '--commit': | |
208 | $parsed['doCommit'] = TRUE; | |
209 | break; | |
210 | ||
211 | case '--no-commit': | |
212 | $parsed['doCommit'] = FALSE; | |
213 | break; | |
214 | ||
bd9556c5 TO |
215 | case '--sql': |
216 | $parsed['doSql'] = TRUE; | |
217 | break; | |
218 | ||
219 | case '--no-sql': | |
220 | $parsed['doSql'] = FALSE; | |
221 | break; | |
222 | ||
b43bfb44 TO |
223 | default: |
224 | if ($arg[0] !== '-') { | |
225 | $positional[] = $arg; | |
226 | } | |
227 | else { | |
228 | fatal("Unrecognized argument: $arg\n"); | |
229 | } | |
230 | break; | |
231 | } | |
232 | } | |
233 | ||
234 | foreach ($positional as $offset => $value) { | |
235 | $name = $positions[$offset] ?? "unknown_$offset"; | |
236 | $parsed[$name] = $value; | |
237 | } | |
238 | ||
239 | if (!isset($parsed['doCommit'])) { | |
240 | fatal("Must specify --commit or --no-commit\n"); | |
241 | } | |
242 | ||
243 | return $parsed; | |
244 | } | |
68122019 TO |
245 | |
246 | /** | |
247 | * @return array | |
248 | * Ex: ['ext/afform/html/info.xml', 'ext/search_kit/info.xml'] | |
249 | */ | |
250 | function findCoreInfoXml() { | |
251 | $lines = explode("\n", file_get_contents('distmaker/core-ext.txt')); | |
252 | $exts = preg_grep(";^#;", $lines, PREG_GREP_INVERT); | |
253 | $exts = preg_grep(';[a-z-A-Z];', $exts); | |
254 | ||
255 | $infoXmls = []; | |
256 | foreach ($exts as $coreExtDir) { | |
257 | $infoXmls = array_merge($infoXmls, (array) glob("ext/$coreExtDir/*/info.xml")); | |
258 | if (file_exists("ext/$coreExtDir/info.xml")) { | |
259 | $infoXmls[] = "ext/$coreExtDir/info.xml"; | |
260 | } | |
261 | } | |
262 | ||
263 | return $infoXmls; | |
264 | } |