Syncing
php artisan docs:sync
For each registered project, before syncing: if docs.sync.auto_discover_versions
is enabled (it is by default), checks the project's latest GitHub release and
registers/updates it as the default version — see
registering-projects.md.
Then, if docs.sync.keep_versions is set above 0, prunes old non-default
versions beyond that count (see registering-projects.md).
Then, for every registered version: fetches the repository's git tree at
that version's ref, prunes documents whose file no longer exists remotely
(unless docs.sync.prune_missing is false), then fetches and upserts any
file whose git blob SHA has changed since the last sync. Unchanged files are
skipped entirely — their content is never re-fetched.
Finally, it stores the project's root files, such as its README and CHANGELOG (see Project files below).
A version's last_synced_at/last_synced_sha are only updated once its sync
completes without error, so a failed run never marks a version as up to
date. Each version of a project syncs independently, so v1.0.0 and v2.0.0
can be at different commits, synced at different times.
A GitHub error, such as a revoked token or a rate limit, doesn't stop the
run either. A failed release lookup is reported and keeps the versions
already registered. A version that fails to sync is reported, keeps its
documents and is retried on the next run, while the remaining versions and
projects still sync. docs:sync then exits with a failure status, so a
scheduled run still shows the error. A queued project's job reports it and
completes, so the rest of the chain keeps going.
Project files
docs:sync also stores a few files from the root of each project's
repository: by default README.md, CHANGELOG.md and NEWS.md
(docs.sync.additional_files). Paths match case-insensitively, so a repository's
Readme.md or NEWS.MD is picked up too, and a file a repository doesn't
have is simply skipped. They're read at the default version's ref, or at
HEAD while the project has no versions, so projects without any releases
get them as well. Local projects read them from disk.
Like documents, only files whose git blob SHA changed are downloaded again, and a file that's gone from the repository is deleted. A failed fetch is reported and keeps what was stored last time, so it never fails the sync.
Each file is stored as a Document of type File. It belongs to the
project instead of a version, so it's never part of a version's pages,
and it's synced with searchable false, so it never shows up in search:
$project->file('README.md')?->toHtml(); // rendered and cached like any document
$project->file('changelog.md')?->body; // raw markdown; the path matches any case
$project->files; // every synced root file
Set docs.sync.additional_files to [] to turn it off.
Pass --project={slug} to sync a single project instead of every registered
one — useful right after registering or updating just that project:
php artisan docs:sync --project=laravel-podman
Schedule it, e.g. in routes/console.php:
use Illuminate\Support\Facades\Schedule;
Schedule::command('docs:sync')->daily();
There is no webhook listener — sync is manual or scheduled only.
Queued syncing
By default docs:sync runs inline and blocks until every project has
synced. Pass --queue to dispatch it instead, or set
docs.sync.queue.enabled to true to make queuing the default for every
run (use --sync on a given run to force an inline run regardless):
php artisan docs:sync --queue
This chains one SyncProjectDocuments job per project, followed by a job
that rebuilds the search index once every project has synced. Chained jobs
run one at a time, in that order — never in parallel — regardless of how
many queue workers are running, so projects never compete for the same
GitHub API rate limit.
Each project's job is protected against overlapping with another sync of
the same project (e.g. an overlapping schedule run, or docs:sync --queue invoked twice before the first finishes) — a duplicate is released
back onto the queue to retry instead of running alongside the first. Tune
this via docs.sync.queue.overlap_release_after /
docs.sync.queue.overlap_expires_after, and the connection/queue name jobs
are dispatched on via docs.sync.queue.connection / docs.sync.queue.queue
— see configuration.md.
