Skip to content

Commit 6cfa659

Browse files
committed
docs(app_development): add occ commands guide
Closes #5460 Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
1 parent 750d035 commit 6cfa659

2 files changed

Lines changed: 175 additions & 0 deletions

File tree

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
.. _occ_commands:
2+
3+
============
4+
occ commands
5+
============
6+
7+
Nextcloud apps can register custom :ref:`occ <occ>` commands that administrators can run from the command line. Commands extend ``OC\Core\Command\Base``, which wraps
8+
`Symfony Console <https://symfony.com/doc/current/console.html>`_ and adds bash completion support, so the full Symfony
9+
Console API is available.
10+
11+
12+
Registering a command
13+
---------------------
14+
15+
List every command class in ``appinfo/info.xml`` under the ``<commands>`` element:
16+
17+
.. code-block:: xml
18+
:caption: appinfo/info.xml
19+
20+
<info>
21+
...
22+
<commands>
23+
<command>OCA\MyApp\Command\Greet</command>
24+
</commands>
25+
</info>
26+
27+
Nextcloud reads this list at startup and wires each class through the dependency
28+
injection container, so constructor injection works automatically.
29+
30+
31+
Creating a command class
32+
------------------------
33+
34+
Place command classes in ``lib/Command/``. Each class must extend
35+
``OC\Core\Command\Base`` and implement two methods:
36+
37+
- ``configure()`` — declare the name, description, arguments, and options.
38+
- ``execute()`` — run the command logic and return an exit code.
39+
40+
.. code-block:: php
41+
:caption: lib/Command/Greet.php
42+
43+
<?php
44+
45+
declare(strict_types=1);
46+
47+
namespace OCA\MyApp\Command;
48+
49+
use OC\Core\Command\Base;
50+
use OCP\IUserManager;
51+
use Symfony\Component\Console\Input\InputArgument;
52+
use Symfony\Component\Console\Input\InputInterface;
53+
use Symfony\Component\Console\Input\InputOption;
54+
use Symfony\Component\Console\Output\OutputInterface;
55+
56+
class Greet extends Base {
57+
58+
public function __construct(
59+
private IUserManager $userManager,
60+
) {
61+
parent::__construct();
62+
}
63+
64+
#[\Override]
65+
protected function configure(): void {
66+
$this
67+
->setName('myapp:greet')
68+
->setDescription('Print a greeting for a Nextcloud user')
69+
->addArgument(
70+
'user-id',
71+
InputArgument::REQUIRED,
72+
'The user to greet',
73+
)
74+
->addOption(
75+
'shout',
76+
null,
77+
InputOption::VALUE_NONE,
78+
'Print the greeting in uppercase',
79+
);
80+
}
81+
82+
#[\Override]
83+
protected function execute(InputInterface $input, OutputInterface $output): int {
84+
$userId = $input->getArgument('user-id');
85+
$user = $this->userManager->get($userId);
86+
87+
if ($user === null) {
88+
$output->writeln("<error>User \"$userId\" not found.</error>");
89+
return self::FAILURE;
90+
}
91+
92+
$greeting = 'Hello, ' . $user->getDisplayName() . '!';
93+
94+
if ($input->getOption('shout')) {
95+
$greeting = strtoupper($greeting);
96+
}
97+
98+
$output->writeln($greeting);
99+
return self::SUCCESS;
100+
}
101+
}
102+
103+
Command naming
104+
--------------
105+
106+
Use ``appid:command-name`` as the command name. For apps with many commands, group them
107+
with an extra segment: ``appid:group:command-name``. Names must be lowercase and use
108+
hyphens as word separators.
109+
110+
111+
Arguments and options
112+
---------------------
113+
114+
Arguments are positional. Options are prefixed with ``--``.
115+
116+
+-----------------------------------------+-------------------------------------------+
117+
| Constant | Meaning |
118+
+=========================================+===========================================+
119+
| ``InputArgument::REQUIRED`` | Argument must be provided |
120+
+-----------------------------------------+-------------------------------------------+
121+
| ``InputArgument::OPTIONAL`` | Argument may be omitted |
122+
+-----------------------------------------+-------------------------------------------+
123+
| ``InputArgument::IS_ARRAY`` | Argument accepts multiple values |
124+
+-----------------------------------------+-------------------------------------------+
125+
| ``InputOption::VALUE_NONE`` | Flag — present or absent, no value |
126+
+-----------------------------------------+-------------------------------------------+
127+
| ``InputOption::VALUE_REQUIRED`` | Option requires a value |
128+
+-----------------------------------------+-------------------------------------------+
129+
| ``InputOption::VALUE_OPTIONAL`` | Option value is optional |
130+
+-----------------------------------------+-------------------------------------------+
131+
| ``InputOption::VALUE_IS_ARRAY`` | Option can be repeated |
132+
+-----------------------------------------+-------------------------------------------+
133+
134+
See the `Symfony Console documentation <https://symfony.com/doc/current/console/input.html>`_
135+
for the full reference.
136+
137+
138+
Return codes
139+
------------
140+
141+
``execute()`` must return an integer. Use the constants defined by
142+
``OC\Core\Command\Base`` (inherited from Symfony):
143+
144+
- ``self::SUCCESS`` (``0``) — command completed successfully.
145+
- ``self::FAILURE`` (``1``) — command encountered an error.
146+
- ``self::INVALID`` (``2``) — command was called with invalid input.
147+
148+
149+
Interactive commands
150+
--------------------
151+
152+
Commands can ask for confirmation or prompt for values using Symfony's
153+
`question helper <https://symfony.com/doc/current/components/console/helpers/questionhelper.html>`_:
154+
155+
.. code-block:: php
156+
157+
use Symfony\Component\Console\Helper\QuestionHelper;
158+
use Symfony\Component\Console\Question\ConfirmationQuestion;
159+
160+
// In execute():
161+
/** @var QuestionHelper $helper */
162+
$helper = $this->getHelper('question');
163+
$question = new ConfirmationQuestion('Are you sure? (y/n) ', false);
164+
165+
if (!$helper->ask($input, $output, $question)) {
166+
$output->writeln('Aborted.');
167+
return self::FAILURE;
168+
}
169+
170+
.. note::
171+
172+
Interactive prompts are skipped when occ is run non-interactively (e.g. from a
173+
cron job). Guard against this with ``$input->isInteractive()`` or use
174+
``--yes``/``--no`` options so administrators can automate the command.

developer_manual/app_development/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ App development
1111
info
1212
init
1313
dependency_management
14+
commands
1415
dav_extension
1516
translation

0 commit comments

Comments
 (0)