packager: allow overrideing package info from cli
[pharext/pharext] / src / pharext / Updater.php
1 <?php
2
3 namespace pharext;
4
5 use Phar;
6 use PharFileInfo;
7 use SplFileInfo;
8 use pharext\Exception;
9
10 class Updater implements Command
11 {
12 use Cli\Command;
13
14 /**
15 * Create the command
16 */
17 public function __construct() {
18 $this->args = new Cli\Args([
19 ["h", "help", "Display this help",
20 Cli\Args::OPTIONAL|Cli\Args::SINGLE|Cli\Args::NOARG|Cli\Args::HALT],
21 ["v", "verbose", "More output",
22 Cli\Args::OPTIONAL|Cli\Args::SINGLE|Cli\Args::NOARG],
23 ["q", "quiet", "Less output",
24 Cli\Args::OPTIONAL|Cli\Args::SINGLE|Cli\Args::NOARG],
25 [null, "signature", "Show pharext signature",
26 Cli\Args::OPTIONAL|Cli\Args::SINGLE|Cli\Args::NOARG|Cli\Args::HALT],
27 [null, "license", "Show pharext license",
28 Cli\Args::OPTIONAL|Cli\Args::SINGLE|Cli\Args::NOARG|Cli\Args::HALT],
29 [null, "version", "Show pharext version",
30 Cli\Args::OPTIONAL|Cli\Args::SINGLE|Cli\Args::NOARG|Cli\Args::HALT],
31 [0, "path", "Path to .ext.phar to update",
32 Cli\Args::REQUIRED|Cli\Args::MULTI],
33 ]);
34 }
35
36 /**
37 * @inheritdoc
38 * @see \pharext\Command::run()
39 */
40 public function run($argc, array $argv) {
41 $errs = [];
42 $prog = array_shift($argv);
43 foreach ($this->args->parse(--$argc, $argv) as $error) {
44 $errs[] = $error;
45 }
46
47 if ($this->args["help"]) {
48 $this->header();
49 $this->help($prog);
50 exit;
51 }
52
53 try {
54 foreach (["signature", "license", "version"] as $opt) {
55 if ($this->args[$opt]) {
56 printf("%s\n", $this->metadata($opt));
57 exit;
58 }
59 }
60 } catch (\Exception $e) {
61 $this->error("%s\n", $e->getMessage());
62 exit(self::EARGS);
63 }
64
65
66 foreach ($this->args->validate() as $error) {
67 $errs[] = $error;
68 }
69
70 if ($errs) {
71 if (!$this->args["quiet"]) {
72 $this->header();
73 }
74 foreach ($errs as $err) {
75 $this->error("%s\n", $err);
76 }
77 printf("\n");
78 if (!$this->args["quiet"]) {
79 $this->help($prog);
80 }
81 exit(self::EARGS);
82 }
83
84 foreach ($this->args[0] as $file) {
85 $info = new SplFileInfo($file);
86
87 while ($info->isLink()) {
88 $info = new SplFileInfo($info->getLinkTarget());
89 }
90
91 if ($info->isFile()) {
92 if (!$this->updatePackage($info)) {
93 $this->warn("Cannot upgrade pre-v3 packages\n");
94 }
95 } else {
96 $this->error("File '%s' does not exist\n", $file);
97 exit(self::EARGS);
98 }
99 }
100 }
101
102 /**
103 * Replace the pharext core in an .ext.phar package
104 * @param string $temp path to temp phar
105 * @return boolean FALSE if the package is too old (pre-v3) to upgrade
106 */
107 private function replacePharext($temp) {
108 $phar = new Phar($temp, Phar::CURRENT_AS_SELF);
109 $phar->startBuffering();
110
111 if (!$meta = $phar->getMetadata()) {
112 // don't upgrade pre-v3 packages
113 return false;
114 }
115
116 // replace current pharext files
117 $core = (new Task\BundleGenerator)->run($this->verbosity());
118 $phar->buildFromIterator($core);
119 $stub = __DIR__."/../pharext_installer.php";
120 (new Task\PharStub($phar, $stub))->run($this->verbosity());
121
122 // check dependencies
123 foreach ($phar as $info) {
124 if (fnmatch("*.ext.phar*", $info->getBasename())) {
125 $this->updatePackage($info, $phar);
126 }
127 }
128
129 $phar->stopBuffering();
130
131 $phar->setMetadata([
132 "version" => Metadata::version(),
133 "header" => Metadata::header(),
134 ] + $meta);
135
136 $this->info("Updated pharext version from '%s' to '%s'\n",
137 isset($meta["version"]) ? $meta["version"] : "(unknown)",
138 $phar->getMetadata()["version"]);
139
140 return true;
141 }
142
143 /**
144 * Update an .ext.phar package to the current pharext version
145 * @param SplFileInfo $file
146 * @param Phar $phar the parent phar containing $file as dependency
147 * @return boolean FALSE if the package is too old (pre-v3) to upgrade
148 * @throws Exception
149 */
150 private function updatePackage(SplFileInfo $file, Phar $phar = null) {
151 $this->info("Updating pharext core in '%s'...\n", basename($file));
152
153 $temp = new Tempname("update", substr(strstr($file, ".ext.phar"), 4));
154
155 if (!copy($file->getPathname(), $temp)) {
156 throw new Exception;
157 }
158 if (!chmod($temp, $file->getPerms())) {
159 throw new Exception;
160 }
161
162 if (!$this->replacePharext($temp)) {
163 return false;
164 }
165
166 if ($phar) {
167 $phar->addFile($temp, $file);
168 } elseif (!rename($temp, $file->getPathname())) {
169 throw new Exception;
170 }
171
172 return true;
173 }
174 }