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