-
Notifications
You must be signed in to change notification settings - Fork 83
Add Environment Sync guide section #797
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
5d9e6d9
Add Environment Sync guide section
bryantgillespie 8a23110
Restructure Environment Sync docs around the user journey
bryantgillespie 2c67ee9
Address review feedback: show deletion prompts, fix mirror diff mode
bryantgillespie 82ae732
moar wip
bryantgillespie 15dc761
hopefully last wip
bryantgillespie 63af06b
cleanup
bryantgillespie 5235d2c
tweak docs
bryantgillespie b766511
Scoped pulls up front, admin gate, and permission warning accuracy
bryantgillespie 0c5b9f3
Clarity pass; move Environment Sync above Deployments in guides
bryantgillespie 61e0ac0
cli output component
bryantgillespie b92da79
Merge branch 'main' into bry/cli-env-sync-docs
bryantgillespie fefe49f
Update content/guides/10.environment-sync/5.secrets-and-limitations.md
bryantgillespie ecda73c
tweaks
bryantgillespie File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| <script setup lang="ts"> | ||
| import UiProsePre from "@nuxt/ui/components/prose/Pre.vue"; | ||
| import { computed } from "vue"; | ||
|
|
||
| defineOptions({ name: "ProsePre", inheritAttrs: false }); | ||
|
|
||
| const props = defineProps<{ | ||
| icon?: string; | ||
| code?: string; | ||
| language?: string; | ||
| filename?: string; | ||
| highlights?: number[]; | ||
| hideHeader?: boolean; | ||
| meta?: string; | ||
| class?: unknown; | ||
| }>(); | ||
|
|
||
| const isTerminal = computed(() => props.language === "cli"); | ||
|
|
||
| interface Span { | ||
| text: string; | ||
| class?: string; | ||
| } | ||
|
|
||
| // Mirrors the CLI's own painting (packages/cli/src/kernel/ui.ts): colored status | ||
| // glyphs, green + / yellow ~ plan tokens, whole-line red deletions with a red | ||
| // `✖N deleted` tail, and dimmed hint lines under an error. | ||
| const STATUS_GLYPHS: Record<string, string> = { | ||
| "●": "text-cyan-400", | ||
| "◇": "text-green-400", | ||
| "▲": "text-yellow-400", | ||
| "✖": "text-red-400", | ||
| }; | ||
|
|
||
| function paint(line: string, inErrorHint: boolean): Span[] { | ||
| if (inErrorHint) return [{ text: line, class: "text-neutral-500" }]; | ||
| if (line.startsWith("✖ DELETE")) | ||
| return [{ text: line, class: "text-red-400" }]; | ||
|
|
||
| if (line.startsWith("+")) { | ||
| return [{ text: "+", class: "text-green-400" }, { text: line.slice(1) }]; | ||
| } | ||
|
|
||
| if (line.startsWith("~")) { | ||
| const rest = line.slice(1); | ||
| const tail = rest.indexOf("✖"); | ||
|
|
||
| if (tail !== -1 && !rest.slice(tail).startsWith("✖0 ")) { | ||
| return [ | ||
| { text: "~", class: "text-yellow-400" }, | ||
| { text: rest.slice(0, tail) }, | ||
| { text: rest.slice(tail), class: "text-red-400" }, | ||
| ]; | ||
| } | ||
|
|
||
| return [{ text: "~", class: "text-yellow-400" }, { text: rest }]; | ||
| } | ||
|
|
||
| const glyphClass = STATUS_GLYPHS[line.charAt(0)]; | ||
| if (glyphClass && line.charAt(1) === " ") | ||
| return [ | ||
| { text: line.charAt(0), class: glyphClass }, | ||
| { text: line.slice(1) }, | ||
| ]; | ||
|
|
||
| return [{ text: line }]; | ||
| } | ||
|
|
||
| const lines = computed<Span[][]>(() => { | ||
| const raw = (props.code ?? "").replace(/\n$/, "").split("\n"); | ||
| const painted: Span[][] = []; | ||
| let inErrorHint = false; | ||
|
|
||
| for (const line of raw) { | ||
| if (!line.startsWith(" ")) | ||
| inErrorHint = line.startsWith("✖ ") && !line.startsWith("✖ DELETE"); | ||
| painted.push(paint(line, inErrorHint && line.startsWith(" "))); | ||
| } | ||
|
|
||
| return painted; | ||
| }); | ||
| </script> | ||
|
|
||
| <template> | ||
| <div | ||
| v-if="isTerminal" | ||
| class="not-prose my-5 overflow-hidden rounded-lg border border-neutral-800 bg-neutral-950 font-mono text-xs/5 text-neutral-200" | ||
| > | ||
| <div | ||
| class="relative flex items-center justify-center border-b border-neutral-800 px-4 py-2.5" | ||
| > | ||
| <div class="absolute left-4 flex gap-1.5" aria-hidden="true"> | ||
| <span class="size-2.5 rounded-full bg-neutral-700" /> | ||
| <span class="size-2.5 rounded-full bg-neutral-700" /> | ||
| <span class="size-2.5 rounded-full bg-neutral-700" /> | ||
| </div> | ||
| <span class="text-[11px] text-neutral-400">{{ | ||
| filename || "Terminal" | ||
| }}</span> | ||
| </div> | ||
| <pre | ||
| class="px-4 py-3.5 whitespace-pre-wrap break-words" | ||
| v-bind="$attrs" | ||
| ><code><template v-for="(line, index) in lines" :key="index"><span v-for="(span, spanIndex) in line" :key="spanIndex" :class="span.class">{{ span.text }}</span>{{ '\n' }}</template></code></pre> | ||
| </div> | ||
| <UiProsePre | ||
| v-else | ||
| :icon="icon" | ||
| :code="code" | ||
| :language="language" | ||
| :filename="filename" | ||
| :highlights="highlights" | ||
| :hide-header="hideHeader" | ||
| :meta="meta" | ||
| :class="props.class" | ||
| v-bind="$attrs" | ||
| > | ||
| <slot /> | ||
| </UiProsePre> | ||
| </template> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| title: Environment Sync |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| --- | ||
| stableId: 393b999d-c4ed-4b83-9208-cf088a4918ab | ||
| title: Overview | ||
| description: Move schema and configuration between Directus instances through sync files you commit to git, using the Directus CLI. | ||
| --- | ||
|
|
||
| You build your data model on a development instance, and at some point that work has to reach staging and production. Environment Sync makes that a git workflow: the Directus CLI (`directus-cli`, or its short alias `d6s`) writes an instance's **Schema and Configuration** to JSON sync files you commit, then applies those files to any instance you point it at. | ||
|
|
||
| ```bash | ||
| d6s sync pull --from staging # write the instance's Schema + Configuration to sync files | ||
| d6s sync diff --to production # preview what pushing the sync files would change | ||
| d6s sync push --to production # apply them | ||
| ``` | ||
|
|
||
| Because the sync files live in your repository, you can review changes in pull requests, promote them through git history, and use a revert plus another push to restore an earlier state. `d6s sync` on its own runs a small interactive wizard that pulls and pushes in one pass. | ||
|
|
||
| The CLI is built to be safe to point at production: `diff` never applies anything, deletions happen only in `mirror` mode behind their own explicit consent (that includes removals during a rollback), and the CLI asks you to resolve records that match more than one target record. It never guesses. [How It Works](/guides/environment-sync/how-it-works) covers the full safety model. | ||
|
|
||
| ## What syncs | ||
|
|
||
| - **Schema**: every collection, field, and relation, including custom fields on system collections. | ||
| - **Configuration**: roles, policies, access, permissions, flows, operations, dashboards, panels, settings, media-library folders, and custom Data Studio translation strings. | ||
| - **Opt-in**: user accounts, with every secret field stripped. | ||
|
|
||
| Unlike a full schema snapshot, a pull is not all-or-nothing. `--collections posts` narrows which Schema files it overwrites, and resource flags like `--flows` or `--no-schema` choose which Configuration files it touches. That is how you promote finished work from a shared development instance while unfinished work stays out of the sync files entirely. Scope is by collection and resource type, not by individual record. See [Promote only the changes that are ready](/guides/environment-sync/common-workflows#promote-only-the-changes-that-are-ready). | ||
|
|
||
| Records in your own collections are **content**, and Environment Sync does not sync them. It moves the shape of a project and its configuration, not its content. | ||
|
|
||
| ::callout{icon="i-lucide-info"} | ||
| Content sync is deferred to a future release. Cross-instance record identity for integer primary keys, file references, and user references needs its own architecture, and a wrong guess there could overwrite content on the target. | ||
| :: | ||
|
|
||
| ## Before you start | ||
|
|
||
| - **Both instances must run Directus 12.2.0 or later, at the same exact version and on the same database vendor.** The patch release must match. The server refuses an incompatible schema comparison: some patches change the snapshot format, and database vendors describe column types differently. `--allow-drift` bypasses the check when you accept that risk; see [the compatibility gate](/guides/environment-sync/how-it-works#the-compatibility-gate). | ||
| - **An admin credential for each instance.** A static token from an admin user is the usual choice. The CLI verifies this and refuses non-admin tokens: the server rejects non-admin schema and import writes, and non-admin reads are silently filtered by permissions, which would produce sync files that look complete but aren't. | ||
| - **A git repository.** The sync files are designed for review and versioning. Any repository works; many teams use the one that already holds their Directus deployment configuration. | ||
|
|
||
| ## Where to go | ||
|
|
||
| ::card-group | ||
|
|
||
| :::card{title="Quickstart" icon="i-lucide-rocket" to="/guides/environment-sync/quickstart"} | ||
| Run the full pull, diff, push loop against two throwaway instances and see every command's output. | ||
| ::: | ||
|
|
||
| :::card{title="How It Works" icon="i-lucide-lightbulb" to="/guides/environment-sync/how-it-works"} | ||
| The mental model: sync files as the source of truth, record identity, push phases, and the safety rules. | ||
| ::: | ||
|
|
||
| :::card{title="Common Workflows" icon="i-lucide-map" to="/guides/environment-sync/common-workflows"} | ||
| Promote changes, ship only what's ready, adopt sync on an existing project, roll back, recover from drift. | ||
| ::: | ||
|
|
||
| :::card{title="CI & Automation" icon="i-lucide-bot" to="/guides/environment-sync/ci-and-automation"} | ||
| Post the production diff on pull requests and push on merge, with tokens and JSON reports. | ||
| ::: | ||
|
|
||
| :::card{title="Secrets & Limitations" icon="i-lucide-shield-check" to="/guides/environment-sync/secrets-and-limitations"} | ||
| How secret values are kept out of your repository, and what Environment Sync deliberately does not do. | ||
| ::: | ||
|
|
||
| :::card{title="Reference" icon="i-lucide-list-checks" to="/guides/environment-sync/reference"} | ||
| Every command, flag, table, and report format in one place. | ||
| ::: | ||
|
|
||
| :: | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.