@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorConfigManagementSetWorkflow
4 extends PhabricatorConfigManagementWorkflow {
5
6 protected function didConstruct() {
7 $this
8 ->setName('set')
9 ->setExamples(
10 "**set** __key__ __value__\n".
11 "**set** __key__ --stdin < value.json")
12 ->setSynopsis(pht('Set a local configuration value.'))
13 ->setArguments(
14 array(
15 array(
16 'name' => 'database',
17 'help' => pht(
18 'Update configuration in the database instead of '.
19 'in local configuration.'),
20 ),
21 array(
22 'name' => 'stdin',
23 'help' => pht('Read option value from stdin.'),
24 ),
25 array(
26 'name' => 'args',
27 'wildcard' => true,
28 ),
29 ));
30 }
31
32 public function execute(PhutilArgumentParser $args) {
33 $argv = $args->getArg('args');
34 if (!$argv) {
35 throw new PhutilArgumentUsageException(
36 pht('Specify the configuration key you want to set.'));
37 }
38
39 $is_stdin = $args->getArg('stdin');
40
41 $key = $argv[0];
42
43 if ($is_stdin) {
44 if (count($argv) > 1) {
45 throw new PhutilArgumentUsageException(
46 pht(
47 'Too many arguments: expected only a configuration key when '.
48 'using "--stdin".'));
49 }
50
51 fprintf(STDERR, tsprintf("%s\n", pht('Reading value from stdin...')));
52 $value = file_get_contents('php://stdin');
53 } else {
54 if (count($argv) == 1) {
55 throw new PhutilArgumentUsageException(
56 pht(
57 'Specify a value to set the configuration key "%s" to, or '.
58 'use "--stdin" to read a value from stdin.',
59 $key));
60 }
61
62 if (count($argv) > 2) {
63 throw new PhutilArgumentUsageException(
64 pht(
65 'Too many arguments: expected one key and one value.'));
66 }
67
68 $value = $argv[1];
69 }
70
71 $options = PhabricatorApplicationConfigOptions::loadAllOptions();
72 if (empty($options[$key])) {
73 throw new PhutilArgumentUsageException(
74 pht(
75 'Configuration key "%s" is unknown. Use "bin/config list" to list '.
76 'all known keys.',
77 $key));
78 }
79
80 $option = $options[$key];
81
82 $type = $option->newOptionType();
83 if ($type) {
84 try {
85 $value = $type->newValueFromCommandLineValue(
86 $option,
87 $value);
88 $type->validateStoredValue($option, $value);
89 } catch (PhabricatorConfigValidationException $ex) {
90 throw new PhutilArgumentUsageException($ex->getMessage());
91 }
92 } else {
93 // NOTE: For now, this handles both "wild" values and custom types.
94 $type = $option->getType();
95 switch ($type) {
96 default:
97 $value = json_decode($value, true);
98 if (!is_array($value)) {
99 switch ($type) {
100 default:
101 $message = pht(
102 'Configuration key "%s" is of type "%s". Specify it in JSON.',
103 $key,
104 $type);
105 break;
106 }
107 throw new PhutilArgumentUsageException($message);
108 }
109 break;
110 }
111 }
112
113 $use_database = $args->getArg('database');
114 if ($option->getLocked() && $use_database) {
115 throw new PhutilArgumentUsageException(
116 pht(
117 'Config key "%s" is locked and can only be set in local '.
118 'configuration. To learn more, see "%s" in the documentation.',
119 $key,
120 pht('Configuration Guide: Locked and Hidden Configuration')));
121 }
122
123 try {
124 $option->getGroup()->validateOption($option, $value);
125 } catch (PhabricatorConfigValidationException $validation) {
126 // Convert this into a usage exception so we don't dump a stack trace.
127 throw new PhutilArgumentUsageException($validation->getMessage());
128 }
129
130 if ($use_database) {
131 $config_entry = PhabricatorConfigEntry::loadConfigEntry($key);
132 $config_entry->setValue($value);
133
134 // If the entry has been deleted, resurrect it.
135 $config_entry->setIsDeleted(0);
136
137 $config_entry->save();
138
139 $write_message = pht(
140 'Wrote configuration key "%s" to database storage.',
141 $key);
142 } else {
143 $config_source = new PhabricatorConfigLocalSource();
144
145 $local_path = $config_source->getReadablePath();
146
147 try {
148 $config_source->setKeys(array($key => $value));
149 } catch (FilesystemException $ex) {
150 throw new PhutilArgumentUsageException(
151 pht(
152 'Local path "%s" is not writable. This file must be writable '.
153 'so that "bin/config" can store configuration.',
154 Filesystem::readablePath($local_path)));
155 }
156
157 try {
158 $dbstore = new PhabricatorConfigDatabaseSource('default');
159 $dbval = $dbstore->getKeys(array($key));
160 if (!empty($dbval)) {
161 echo tsprintf(
162 "<bg:yellow>** %s **</bg> %s\n",
163 pht('OVERRIDDEN'),
164 pht(
165 'The configuration key "%s" is already defined in the '.
166 'database. The value from the database will override the '.
167 'value in local storage.',
168 $key));
169 }
170 } catch (Throwable $ex) {
171 // The database config is hosed (or we're doing initial setup and
172 // don't have a database yet), just ignore
173 }
174
175 $write_message = pht(
176 'Wrote configuration key "%s" to local storage (in file "%s").',
177 $key,
178 $local_path);
179 }
180
181 echo tsprintf(
182 "<bg:green>** %s **</bg> %s\n",
183 pht('DONE'),
184 $write_message);
185
186 return 0;
187 }
188
189}