/run/caiwu-upstream-sync.lock'); $end = strpos($contents, 'PREVIOUS_IMAGE=', $start ?: 0); $this->assertNotFalse($start, $script); $this->assertNotFalse($end, $script); $this->assertLessThan(strpos($contents, 'docker compose -f compose.jiangling.yml up'), $end); $guard = substr($contents, $start, $end - $start); $directory = sys_get_temp_dir() . '/caiwu-deploy-lock-' . bin2hex(random_bytes(8)); mkdir($directory, 0700); $holder = new Process(['sh', '-c', 'exec 8>"$1"; flock 8; touch "$2"; sleep 2', 'holder', $directory . '/sync.lock', $directory . '/held']); try { $holder->start(); $deadline = microtime(true) + 3; while (! file_exists($directory . '/held') && microtime(true) < $deadline) { usleep(10000); } $this->assertFileExists($directory . '/held'); $guard = str_replace('/run/caiwu-upstream-sync.lock', $directory . '/sync.lock', $guard); $guard .= "touch '$directory/activated'\n"; $timeout = new Process(['sh', '-c', str_replace('-w 1800', '-w 0', $guard)]); $timeout->run(); $this->assertSame(1, $timeout->getExitCode()); $this->assertStringContainsString('application container was not replaced', $timeout->getErrorOutput()); $this->assertFileDoesNotExist($directory . '/activated'); $release = new Process(['sh', '-c', $guard]); $release->start(); usleep(100000); $this->assertTrue($release->isRunning()); $this->assertFileDoesNotExist($directory . '/activated'); $release->wait(); $this->assertTrue($release->isSuccessful(), $release->getErrorOutput()); $this->assertFileExists($directory . '/activated'); } finally { $holder->stop(); foreach (glob($directory . '/*') as $file) { unlink($file); } rmdir($directory); } } } }