mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-24 04:16:20 +00:00
8f12c83d21
ProjectSend prints the update instructions for the way this server was installed, and it knew two answers where it needed three: anything inside a container was handed `docker compose pull && docker compose up -d`. On the Compose stack that builds from a checkout there is no image behind those containers, so `pull` skips every ProjectSend service and `up -d` then finds them all current — the update reports success, changes nothing, and the dashboard goes on offering the same release. Reported by @mueller7382, who stayed on 2.0.0 that way while 2.1.0 was out (#1661). Those installations are now their own kind, told to `git pull` and rebuild, with the two steps a checkout needs that an image does not: its dependencies and its compiled frontend live outside git, so a release that moved either leaves them stale. Two signals decide it, in that order. The published image now declares itself with PROJECTSEND_IMAGE, which is the only evidence an operator bind-mounting over /var/www/html can neither hide nor forge; failing that — images published before this — a working tree in the install directory, which the image never has and the repository's own stack always does. getenv() rather than env(), because a cached configuration makes env() outside a config file return null, and the answer would flip silently on exactly the installs most likely to have cached it. The stale-code banner keeps treating both container kinds alike: what clears it is recreating the container, whichever way its image was built. The changelog also credits the reporter of #1663, which was missed when that entry was written.
90 lines
4.2 KiB
TypeScript
90 lines
4.2 KiB
TypeScript
import { usePage } from '@inertiajs/react';
|
|
import { AlertTriangle } from 'lucide-react';
|
|
|
|
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
|
import { isContainerInstall } from '@/components/update-instructions';
|
|
import { useTranslation } from '@/hooks/use-translation';
|
|
import { type SharedData } from '@/types';
|
|
|
|
/**
|
|
* Says out loud that this server is not running the code it was updated
|
|
* to — the one update failure that has no other symptom.
|
|
*
|
|
* With OPcache configured the way production guides recommend, replacing
|
|
* the files and reloading nothing leaves the database on the new version
|
|
* and every visitor on the old code, indefinitely and without an error
|
|
* anywhere. `projectsend:update` records what it applied; this compares it
|
|
* with what the process rendering this page actually compiled.
|
|
*
|
|
* Deliberately on every staff page rather than the dashboard alone: the
|
|
* person who missed the reload has no reason to suspect anything, so the
|
|
* notice has to find them rather than wait to be visited.
|
|
*/
|
|
export function CodeNoticeBanner() {
|
|
const { t } = useTranslation();
|
|
const { code_notice: notice } = usePage<SharedData>().props;
|
|
|
|
if (!notice) {
|
|
return null;
|
|
}
|
|
|
|
// Both container kinds answer this the same way: what fixes it is
|
|
// recreating the container, whether its image came from a registry or
|
|
// from a build on this machine.
|
|
const container = isContainerInstall(notice.install_kind);
|
|
|
|
// Stated as a fact first, cause second. A deliberate rollback lands
|
|
// here too, and telling somebody to reload PHP-FPM when they meant to
|
|
// go back a version would send them the wrong way.
|
|
const title =
|
|
notice.reason === 'stale_code'
|
|
? t('This server is running version :running, but version :applied is installed', {
|
|
running: notice.running,
|
|
applied: notice.applied,
|
|
})
|
|
: t('Version :running is installed, but the update has not been run', { running: notice.running });
|
|
|
|
return (
|
|
<Alert variant={notice.reason === 'stale_code' ? 'destructive' : 'warning'}>
|
|
<AlertTriangle className="size-4" />
|
|
<AlertTitle>{title}</AlertTitle>
|
|
<AlertDescription className="space-y-2">
|
|
{notice.reason === 'stale_code' ? (
|
|
<>
|
|
<p>
|
|
{container
|
|
? t('The database was updated to :applied, and this container is still running the code it started with.', {
|
|
applied: notice.applied,
|
|
})
|
|
: t(
|
|
'The database was updated to :applied, and PHP is still serving the code it had compiled before that. Reloading it clears this notice.',
|
|
{ applied: notice.applied },
|
|
)}
|
|
</p>
|
|
<Command>{container ? 'docker compose up -d --force-recreate' : 'sudo systemctl reload php8.4-fpm'}</Command>
|
|
{!container && (
|
|
<p className="text-xs">
|
|
{t('If you rolled back on purpose, run php artisan projectsend:update so the database matches the code you restored.')}
|
|
</p>
|
|
)}
|
|
</>
|
|
) : (
|
|
<>
|
|
<p>
|
|
{t(
|
|
'New files are in place, but the database has only been brought up to :applied. Until the update runs, this installation is running code its database does not match.',
|
|
{ applied: notice.applied },
|
|
)}
|
|
</p>
|
|
<Command>{container ? 'docker compose up -d --force-recreate' : 'sudo ./update.sh'}</Command>
|
|
</>
|
|
)}
|
|
</AlertDescription>
|
|
</Alert>
|
|
);
|
|
}
|
|
|
|
function Command({ children }: { children: string }) {
|
|
return <code className="bg-background/60 inline-block rounded px-2 py-1 font-mono text-xs">{children}</code>;
|
|
}
|