• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

diego-ninja / docker / 19601180552

22 Nov 2025 09:03PM UTC coverage: 98.214% (+9.2%) from 88.971%
19601180552

push

github

diego-ninja
docs: facade documentation updated

550 of 560 relevant lines covered (98.21%)

13.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.27
/src/DockerContainerInstance.php
1
<?php
2

3
// ABOUTME: Represents a running Docker container instance with execution capabilities.
4
// ABOUTME: Provides methods to execute commands, manage files, and inspect container state.
5

6
declare(strict_types=1);
7

8
namespace Ninja\Docker;
9

10
use JsonException;
11
use Ninja\Docker\ValueObjects\ContainerPath;
12
use Ninja\Docker\ValueObjects\HostPath;
13
use RuntimeException;
14
use Spatie\Macroable\Macroable;
15
use Symfony\Component\Process\Exception\ProcessFailedException;
16
use Symfony\Component\Process\Process;
17

18
class DockerContainerInstance
19
{
20
    use Macroable;
21

22
    public const string DEFAULT_PATH_AUTHORIZED_KEYS = '/root/.ssh/authorized_keys';
23

24
    public function __construct(
27✔
25
        private readonly DockerContainer $config,
26
        private readonly string $dockerIdentifier,
27
        private readonly string $name
28
    ) {}
27✔
29

30
    public static function fromExisting(string $name): self
1✔
31
    {
32
        return new self(
1✔
33
            config: DockerContainer::create(
1✔
34
                image: self::getImageFromExistingContainer($name),
1✔
35
                name: $name
1✔
36
            ),
1✔
37
            dockerIdentifier: self::getIdFromExistingContainer($name),
1✔
38
            name: $name
1✔
39
        );
1✔
40
    }
41

42
    public static function isRunning(string $name): bool
5✔
43
    {
44
        $process = new Process([
5✔
45
            'docker',
5✔
46
            'ps',
5✔
47
            '-q',
5✔
48
            '-f',
5✔
49
            "name={$name}",
5✔
50
        ]);
5✔
51

52
        $process->run();
5✔
53

54
        return !empty(trim($process->getOutput()));
5✔
55
    }
56

57
    public function __destruct()
20✔
58
    {
59
        if ($this->config->stopOnDestruct) {
20✔
60
            $this->stop();
5✔
61
        }
62
    }
63

64
    public function start(bool $async = false): Process
2✔
65
    {
66
        $fullCommand = $this->config->getStartCommand($this->getShortDockerIdentifier());
2✔
67
        $process     = new Process($fullCommand);
2✔
68

69
        $async ? $process->start() : $process->run();
2✔
70

71
        return $process;
2✔
72
    }
73

74
    public function stop(bool $async = false): Process
13✔
75
    {
76
        $fullCommand = $this->config->getStopCommand($this->getShortDockerIdentifier());
13✔
77
        $process     = new Process($fullCommand);
13✔
78

79
        $async ? $process->start() : $process->run();
13✔
80

81
        return $process;
13✔
82
    }
83

84
    public function getName(): string
5✔
85
    {
86
        return $this->name;
5✔
87
    }
88

89
    public function getConfig(): DockerContainer
1✔
90
    {
91
        return $this->config;
1✔
92
    }
93

94
    public function getDockerIdentifier(): string
4✔
95
    {
96
        return $this->dockerIdentifier;
4✔
97
    }
98

99
    public function getShortDockerIdentifier(): string
20✔
100
    {
101
        return substr($this->dockerIdentifier, 0, 12);
20✔
102
    }
103

104
    public function execute(string $command): string
8✔
105
    {
106
        $dockerCommand = array_merge(
8✔
107
            $this->config->getBaseCommand(),
8✔
108
            ['exec', '--interactive', $this->getShortDockerIdentifier(), $this->config->shell]
8✔
109
        );
8✔
110

111
        $process = new Process($dockerCommand);
8✔
112
        $process->setInput($command);
8✔
113
        $process->setTimeout(null);
8✔
114
        $process->run();
8✔
115

116
        if (!$process->isSuccessful()) {
8✔
117
            throw new RuntimeException(
1✔
118
                "Command execution failed: {$process->getErrorOutput()}"
1✔
119
            );
1✔
120
        }
121

122
        return $process->getOutput();
7✔
123
    }
124

125
    public function addPublicKey(
5✔
126
        HostPath|string $pathToPublicKey,
127
        ContainerPath|string $pathToAuthorizedKeys = self::DEFAULT_PATH_AUTHORIZED_KEYS
128
    ): self {
129
        $publicKeyPath = $pathToPublicKey instanceof HostPath
5✔
130
            ? $pathToPublicKey
1✔
131
            : HostPath::from($pathToPublicKey);
4✔
132

133
        $authorizedKeysPath = $pathToAuthorizedKeys instanceof ContainerPath
4✔
134
            ? $pathToAuthorizedKeys
1✔
135
            : ContainerPath::from($pathToAuthorizedKeys);
3✔
136

137
        $contents = file_get_contents((string)$publicKeyPath);
4✔
138
        if ($contents === false) {
4✔
139
            throw new RuntimeException(
×
140
                sprintf("Could not read contents of public key at %s", (string)$publicKeyPath)
×
141
            );
×
142
        }
143

144
        $publicKeyContents = trim($contents);
4✔
145
        $sshDir            = dirname((string)$authorizedKeysPath);
4✔
146

147
        $this->execute("mkdir -p {$sshDir}");
4✔
148
        $this->execute("chmod 700 {$sshDir}");
4✔
149
        $this->execute("echo '{$publicKeyContents}' >> {$authorizedKeysPath}");
4✔
150
        $this->execute("chmod 600 {$authorizedKeysPath}");
4✔
151
        $this->execute("chown root:root {$authorizedKeysPath}");
4✔
152

153
        return $this;
4✔
154
    }
155

156
    /**
157
     * @param string $fileOrDirectoryOnHost
158
     * @param string $pathInContainer
159
     * @return self
160
     * @throws ProcessFailedException
161
     */
162
    public function addFiles(string $fileOrDirectoryOnHost, string $pathInContainer): self
2✔
163
    {
164
        $fullCommand = $this->config->getCopyCommand($this->getShortDockerIdentifier(), $fileOrDirectoryOnHost, $pathInContainer);
2✔
165

166
        $process = new Process($fullCommand);
2✔
167

168
        $process->run();
2✔
169

170
        if (!$process->isSuccessful()) {
2✔
171
            throw new ProcessFailedException($process);
1✔
172
        }
173

174
        return $this;
1✔
175
    }
176

177
    /**
178
     * @return list<array<string, mixed>>
179
     * @throws JsonException
180
     */
181
    public function inspect(): array
1✔
182
    {
183
        $fullCommand = $this->config->getInspectCommand($this->getShortDockerIdentifier());
1✔
184

185
        $process = new Process($fullCommand);
1✔
186
        $process->run();
1✔
187

188
        $json = trim($process->getOutput());
1✔
189

190
        /** @var list<array<string, mixed>> $result */
191
        $result = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
1✔
192

193
        return $result;
1✔
194
    }
195

196
    private static function getImageFromExistingContainer(string $name): string
1✔
197
    {
198
        $process = new Process([
1✔
199
            'docker',
1✔
200
            'ps',
1✔
201
            '--format={{.Image}}',
1✔
202
            '-f',
1✔
203
            "name={$name}",
1✔
204
        ]);
1✔
205

206
        $process->run();
1✔
207

208
        return trim($process->getOutput());
1✔
209
    }
210

211
    private static function getIdFromExistingContainer(string $name): string
1✔
212
    {
213
        $process = new Process([
1✔
214
            'docker',
1✔
215
            'ps',
1✔
216
            '-q',
1✔
217
            '-f',
1✔
218
            "name={$name}",
1✔
219
        ]);
1✔
220

221
        $process->run();
1✔
222

223
        return trim($process->getOutput());
1✔
224
    }
225

226
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc