diff --git a/CHANGELOG.md b/CHANGELOG.md index d350a2d6..16184052 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ All notable changes to this project will be documented in this file. - Traefik's built-in dashboard is now exposed at `https://traefik.test`, surfacing misconfigured routers directly instead of only in the container logs. Existing installations pick this up after `dde system:down && dde system:up` (a plain restart is not enough — the labels and static `traefik.yml` only apply when the container is recreated). +### Fixed + +- `dde system:install` no longer aborts the "Configuring DNS resolver" step with a cryptic `Permission denied` rename error. A resolver file left behind by dde v1 (which lacked a trailing newline) is now recognised as already configured, so upgrading needs no root. When the file genuinely has to be created, dde prints the exact `sudo` command to run instead of a raw exception. + ## [2.0.0-beta.2] - 2026-06-01 ### Added diff --git a/src/Service/DnsmasqService.php b/src/Service/DnsmasqService.php index 1037666c..4637a91a 100644 --- a/src/Service/DnsmasqService.php +++ b/src/Service/DnsmasqService.php @@ -7,6 +7,7 @@ use App\Manager\DockerManager; use App\Model\ContainerConfig; use App\Util\ProcessFactory; +use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Filesystem; final class DnsmasqService extends AbstractSystemService @@ -114,7 +115,8 @@ public function buildImage(bool $pull = false): void * * On macOS: writes a resolver file to /etc/resolver/test. * On Linux: configures systemd-resolved or NetworkManager. - * Both require root privileges (the installer script runs with sudo). + * Both require root; when dde cannot write the file it explains the + * manual command to run instead. * * @throws \RuntimeException if the platform is not supported */ @@ -219,14 +221,24 @@ private function configureDnsMacOs(): void { $resolverDir = '/etc/resolver'; $resolverFile = $resolverDir.'/test'; - $content = $this->getResolverContent(); - if ($this->filesystem->exists($resolverFile) && $this->filesystem->readFile($resolverFile) === $content) { + // A dde v1 resolver file is content-equivalent but lacks the trailing + // newline; treat it as already configured so upgrades need no root. + if ($this->filesystem->exists($resolverFile) && trim($this->filesystem->readFile($resolverFile)) === trim($content)) { return; } - $this->filesystem->mkdir($resolverDir); - $this->filesystem->dumpFile($resolverFile, $content); + try { + $this->filesystem->mkdir($resolverDir); + $this->filesystem->dumpFile($resolverFile, $content); + } catch (IOException $ioException) { + throw new \RuntimeException(sprintf( + "Could not write %s, which requires root. Create it manually and re-run 'dde system:install':\n echo '%s' | sudo tee %s", + $resolverFile, + rtrim($content, "\n"), + $resolverFile, + ), $ioException->getCode(), previous: $ioException); + } } }