inContainer()) { return InstallationKind::Manual; } return $this->builtFromSource() ? InstallationKind::ContainerSource : InstallationKind::Container; } /** * Protected so a test can answer for it: there is no way to be in a * container and not in one within a single test run. */ protected function inContainer(): bool { // Docker writes the first; Podman writes the second. // // Suppressed, and it has to stay that way. Shared hosting sets // open_basedir to the webspace, and probing a path outside it is a // warning rather than a false — which the framework's error handler // turns into an exception, so the one call that asks which install // this is took the whole dashboard down with it (#1663). Under `@` // the warning is filtered and the probe answers false, which is the // right answer anyway: a host that restricts PHP to a vhost // directory is not the container image. return @file_exists('/.dockerenv') || @file_exists('/run/.containerenv'); } /** * Protected for the same reason as inContainer(), and answered the same * way in tests. */ protected function builtFromSource(): bool { // getenv() rather than env(): once the configuration is cached, // env() outside a config file returns null, and the answer would // silently flip on the installs most likely to have cached it. if (getenv('PROJECTSEND_IMAGE') === '1') { return false; } // A worktree checkout writes .git as a file rather than a // directory, so ask whether it exists, not what it is. return file_exists(base_path('.git')); } }