From c32a0da9c788bbc6d3f250f044b0fbb7a005963c Mon Sep 17 00:00:00 2001 From: Bergfrid Dias Date: Mon, 10 Aug 2026 13:22:47 +0200 Subject: [PATCH 1/5] Add per-PR markdownlint CI check and Automated tests reference (#137) Adapts the old superoffice-docs repo's ci.yml, switched to markdownlint-cli2 and scoped to changed files only. Vale and build validation are covered by Mintlify's own CI Checks add-on and preview deployment, so no custom Action is added for those - see the new contribute/automated-tests.mdx for the full breakdown. Includes a temporary zz-ci-test-delete-me.mdx to confirm the Action triggers on this PR; will be removed before requesting review. --- .github/workflows/markdownlint.yml | 48 ++++++++++++++ config/nav-contribute.json | 3 +- contribute/automated-tests.mdx | 73 ++++++++++++++++++++++ contribute/deployment.mdx | 2 +- contribute/markdown-guide/markdownlint.mdx | 20 +++--- contribute/review/index.mdx | 3 +- contribute/zz-ci-test-delete-me.mdx | 14 +++++ 7 files changed, 151 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/markdownlint.yml create mode 100644 contribute/automated-tests.mdx create mode 100644 contribute/zz-ci-test-delete-me.mdx diff --git a/.github/workflows/markdownlint.yml b/.github/workflows/markdownlint.yml new file mode 100644 index 0000000000..7918aed793 --- /dev/null +++ b/.github/workflows/markdownlint.yml @@ -0,0 +1,48 @@ +name: Markdownlint + +on: + push: + branches: [main] + pull_request: + branches: [main] + +# Automatically cancel in-progress runs on the same branch +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + markdownlint: + name: Lint changed Markdown/MDX files + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get changed files + id: changed + uses: tj-actions/changed-files@v45 + with: + files: | + **/*.md + **/*.mdx + + - name: Set up Node + if: steps.changed.outputs.any_changed == 'true' + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install markdownlint-cli2 + if: steps.changed.outputs.any_changed == 'true' + run: npm install -g markdownlint-cli2 + + - name: Run markdownlint-cli2 on changed files + if: steps.changed.outputs.any_changed == 'true' + continue-on-error: true + run: markdownlint-cli2 ${{ steps.changed.outputs.all_changed_files }} diff --git a/config/nav-contribute.json b/config/nav-contribute.json index 0caea619da..e43fb59840 100644 --- a/config/nav-contribute.json +++ b/config/nav-contribute.json @@ -57,5 +57,6 @@ ] }, "contribute/configure-mintlify", - "contribute/deployment" + "contribute/deployment", + "contribute/automated-tests" ] diff --git a/contribute/automated-tests.mdx b/contribute/automated-tests.mdx new file mode 100644 index 0000000000..d11569f6ac --- /dev/null +++ b/contribute/automated-tests.mdx @@ -0,0 +1,73 @@ +--- +uid: automated-tests +title: Automated tests +description: Every check that runs against a PR or on a schedule for this repo - markdownlint, Vale, build validation, and broken-link checking - what each covers, whether it blocks a merge, and where to look when one flags something. +keywords: ['ci', 'automated tests', 'markdownlint', 'vale', 'broken links', 'mint validate', 'automations'] +author: digitaldiina +date: 08.10.2026 +content_type: concept +language: en +--- + +This page is the single reference for every automatic check that runs against this repo, whether it is triggered by a pull request or runs on its own schedule. It does not duplicate the general mechanics already covered in [Mintlify deployment and CI/CD][1] - only what each check covers and how to act on it. + +## Per-PR checks + +| Check | Runs via | Blocking? | Configured in | +|---|---|---|---| +| Markdownlint | GitHub Action (`.github/workflows/markdownlint.yml`) | No - advisory | This repo, `.markdownlint.yaml` / `.markdownlintignore` | +| Vale | Mintlify CI Checks add-on | No - Warning level | Mintlify dashboard (Add-ons > CI Checks), using this repo's `.vale.ini` | +| Build validation | Mintlify preview deployment | Yes - a failed build shows as a red "Mintlify Deployment" check | Mintlify, automatic on every PR (see [Deploy trigger mechanics][2]) | +| Broken links (internal) | Mintlify CI Checks add-on | No - Warning level | Mintlify dashboard (Add-ons > CI Checks) | + +### Markdownlint + +Lints only the `.md`/`.mdx` files changed in a PR (or pushed directly to `main`) using this repo's own `.markdownlint.yaml` rules. Results appear in the workflow run's log in the GitHub Actions tab - a flagged line does not block the merge, but is worth fixing before requesting review. See [markdownlint][3] for how to interpret and fix a specific rule violation, and how to run the same linter locally. + +### Vale + +Prose-style linting (tone, terminology, formatting), configured entirely by this repo's `.vale.ini` - no separate setup needed once the add-on is toggled on in the Mintlify dashboard. Set to Warning level to match markdownlint's advisory-only posture; results show on the PR's checks list from Mintlify's own GitHub App. + +### Build validation - why there is no separate `mint validate` step + +`mint validate` is a build/schema validator (`docs.json` schema, MDX parse errors, OpenAPI spec validation) - it is unrelated to Vale and does not check broken links or accessibility; those are separate CLI commands (`mint broken-links`, `mint a11y`). It is not run as its own GitHub Action here because the PR preview deployment already performs the equivalent build validation on every PR, live, as part of generating the preview - adding a duplicate `mint validate` Action would just re-check what the preview build already checked. A failed build already surfaces as the red "Mintlify Deployment" check described in [Deployment][2]. + +### Broken links (per-PR) + +Mintlify's Broken Links add-on scans the whole site's internal links (not external URLs) on every PR and reports results the same way as the Vale check above. It is set to Warning level for the same advisory-first reasoning as the other checks. + + +A Mintlify community report (October 2025) describes a case where a page serving a 404 still returned an HTTP 200 status, which could make this check under-report a real broken link. Do not treat "no broken links flagged" here as a guarantee - see the scheduled check and fallback tool below for a second opinion. + + +## Scheduled check: repo-wide broken-link detection + +A per-PR link check only catches breaks introduced by that PR. A separate, Mintlify-hosted automation periodically re-scans the *entire* site, catching drift from any source - including direct admin-bypass pushes that skip PR review entirely. + +* **Type**: `broken-link-detection` (`mint automations create --type broken-link-detection`) +* **Cadence**: `0 6 1,15 * *` - the 1st and 15th of each month, run at 06:00. Standard cron has no native "every 2 weeks" field; this is the closest non-drifting approximation of a biweekly cadence (actual gaps land at 13-16 days depending on the month). +* **Auto-merge**: off. When the automation finds and fixes a broken link, it opens a PR like any other change - it goes through the same manual review as everything else in this repo, it does not merge itself. +* **What it does**: for each broken internal link, it checks whether the target page was renamed or moved and updates the link, or - if the target was removed entirely - edits the surrounding copy to remove or replace the reference. +* **Cost**: billed in Mintlify credits per run (rough public estimate: ~285 credits/run) - a real operating cost, not a free background job. + +See [issue #81][4] for the setup record (creation command, confirmed `mint automations list` output). + +## Fallback: manual broken-link check + +`tools/triage-broken-links.py` (built during [#129][5]) plus the `mint broken-links` CLI remain the trusted manual method for a broken-link sweep, separating genuine breaks from known false positives. Reach for this: + +* Right after a large structural change (a folder move, a nav overhaul) rather than waiting for the next scheduled automation run. +* As a second opinion if the scheduled automation or the per-PR add-on reports "no breaks found" but something looks off - see the HTTP-200-masking-a-404 caveat above. + +## Related content + +* [Mintlify deployment and CI/CD][1] - the deploy pipeline and PR preview mechanics these checks build on. +* [markdownlint][3] - rule details and local usage. +* [Reviewing a PR][6] - the review checklist that links back here. + +[1]: ./deployment +[2]: ./deployment#deploy-trigger-mechanics +[3]: ./markdown-guide/markdownlint +[4]: https://github.com/SuperOfficeDocs/docs/issues/81 +[5]: https://github.com/SuperOfficeDocs/docs/issues/129 +[6]: ./review/index diff --git a/contribute/deployment.mdx b/contribute/deployment.mdx index 1e359e0767..f052264079 100644 --- a/contribute/deployment.mdx +++ b/contribute/deployment.mdx @@ -43,7 +43,7 @@ Requested by the Mintlify GitHub App on `SuperOfficeDocs` (see [Mintlify's GitHu | `deployments` | Read/write - powers PR preview deployments | | `pull requests` | Read/write - lets the web editor create branches/PRs | -## Deploy trigger mechanics +## Deploy trigger mechanics {#deploy-trigger-mechanics} A push to the connected branch (`main`) triggers an automatic production deploy; a pull request against it gets its own preview deployment and check. A manual **Deploy** button in the dashboard exists as a fallback. See [Deployments][2] for the full mechanics - not duplicated here. diff --git a/contribute/markdown-guide/markdownlint.mdx b/contribute/markdown-guide/markdownlint.mdx index 3fa0e6aa1b..73a7ea80c8 100644 --- a/contribute/markdown-guide/markdownlint.mdx +++ b/contribute/markdown-guide/markdownlint.mdx @@ -61,35 +61,37 @@ Don't use HTML comments (``) to turn off a rule for a single l `.markdownlint.yaml` is already tuned to avoid needing most per-file overrides (for example, `MD013`'s line-length limits are generous and `MD041` doesn't require a specific front-matter title). If you hit a rule that genuinely needs a one-off exception, check with the DX team first rather than reaching for an HTML comment — the current MDX-safe mechanism for disabling a rule inline hasn't been established yet. +## Runs automatically on every pull request + +A GitHub Action lints every changed `.md`/`.mdx` file on each PR, using this repo's own `.markdownlint.yaml` and `.markdownlintignore`. It is advisory only for now - a lint issue shows up in the Action's log but does not block the PR from merging. See [Automated tests][8] for the full list of checks that run against this repo and how to read their results. + ## Command-line linting -You can optionally install [markdownlint-cli][6], a command-line interface that allows you to do bulk linting. This is useful for bulk editing but it can also be added to a build pipeline as automatic testing. +You can optionally install [markdownlint-cli2][6], a command-line interface that allows you to do bulk linting - the same tool the pull request Action above uses. This is useful for bulk editing. **Install:** ```sh -npm install -g markdownlint-cli +npm install -g markdownlint-cli2 ``` **Use:** ```sh -markdownlint -f -i *.cs *.xml --disable MD013 MD041 -c .markdownlint.yaml PATH +markdownlint-cli2 PATH ``` -* -f will automatically fix problems that have a defined fix, for example, whitespace -* -i ignores whatever follows, in this case, all cs and XML files. To omit API from the run, add api/ to the path. -* --disable turns off the listed rules -* -c specifies the markdownlint config file. In this case, we use the one inside the `superoffice-docs` repo +`markdownlint-cli2` auto-discovers `.markdownlint.yaml` (the rule configuration) and `.markdownlintignore` (excluded paths, such as generated reference content) from the repo root - no need to repeat them as flags. `PATH` can be a single file, a glob, or a space-separated list of files. Add `--fix` to automatically resolve issues that have a defined fix, such as whitespace. -See [GitHub issue 373][7] regarding why we exclude rules MD013 and MD041. +See [GitHub issue 373][7] regarding why we exclude rules MD013 and MD041 in `.markdownlint.yaml`. [2]: ./index [3]: https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint [4]: https://github.com/DavidAnson/markdownlint#rules--aliases [5]: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md -[6]: https://github.com/igorshubovych/markdownlint-cli +[6]: https://github.com/DavidAnson/markdownlint-cli2 [7]: https://github.com/SuperOfficeDocs/superoffice-docs/issues/373 +[8]: ../automated-tests [img1]: /media/loc/en/contribute/mdlint-list-warning.png [img2]: /media/loc/en/contribute/mdlint-list-hover.png diff --git a/contribute/review/index.mdx b/contribute/review/index.mdx index b0e2392e65..21a45db76a 100644 --- a/contribute/review/index.mdx +++ b/contribute/review/index.mdx @@ -49,7 +49,7 @@ GitHub blocks self-approval of your own PR at the API level, so the Code Owner c * [Markdown formatting][3] - no linting errors * [Style guide][2] -* Automatic tests should pass. +* Automatic tests should pass - see [Automated tests][9] for the full list (markdownlint, Vale, broken-link checks) and where to read their results. Markdownlint and Vale are advisory only for now: a flagged issue is worth a look, but it does not block the merge on its own. * The [Mintlify preview deployment][8] should build successfully, and the changed pages should look correct in it. * Reference to deleted pages should be removed from toc and other content. * Reference to moved or renamed pages should be updated so no links break. @@ -63,3 +63,4 @@ GitHub blocks self-approval of your own PR at the API level, so the Code Owner c [6]: https://github.com/SuperOfficeDocs/docs/blob/main/.github/CODEOWNERS [7]: ../branch-strategy [8]: ./check-changes#preview +[9]: ../automated-tests diff --git a/contribute/zz-ci-test-delete-me.mdx b/contribute/zz-ci-test-delete-me.mdx new file mode 100644 index 0000000000..622fbfb032 --- /dev/null +++ b/contribute/zz-ci-test-delete-me.mdx @@ -0,0 +1,14 @@ +--- +uid: zz-ci-test-delete-me +title: CI test - delete me +description: Temporary file used to confirm the markdownlint Action triggers on a PR. Removed before this PR is marked ready for review. +keywords: ['ci', 'test'] +author: digitaldiina +date: 08.10.2026 +content_type: concept +language: en +--- + +#Heading without a space after the hash + +This deliberately violates MD018 to confirm the markdownlint Action picks it up. From 15c74fd545197fe37058651c631785058326d6bf Mon Sep 17 00:00:00 2001 From: Bergfrid Dias Date: Mon, 10 Aug 2026 13:24:44 +0200 Subject: [PATCH 2/5] Remove CI test file, pin Node 22 in the markdownlint workflow Confirmed on PR #207 that the Action triggers, scopes to changed files, catches lint issues, and reports without blocking the check - removing the deliberate test file now that verification is done. Also bumps setup-node from 20 (deprecated) to 22. --- .github/workflows/markdownlint.yml | 2 +- contribute/zz-ci-test-delete-me.mdx | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) delete mode 100644 contribute/zz-ci-test-delete-me.mdx diff --git a/.github/workflows/markdownlint.yml b/.github/workflows/markdownlint.yml index 7918aed793..1e7f12fe46 100644 --- a/.github/workflows/markdownlint.yml +++ b/.github/workflows/markdownlint.yml @@ -36,7 +36,7 @@ jobs: if: steps.changed.outputs.any_changed == 'true' uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' - name: Install markdownlint-cli2 if: steps.changed.outputs.any_changed == 'true' diff --git a/contribute/zz-ci-test-delete-me.mdx b/contribute/zz-ci-test-delete-me.mdx deleted file mode 100644 index 622fbfb032..0000000000 --- a/contribute/zz-ci-test-delete-me.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -uid: zz-ci-test-delete-me -title: CI test - delete me -description: Temporary file used to confirm the markdownlint Action triggers on a PR. Removed before this PR is marked ready for review. -keywords: ['ci', 'test'] -author: digitaldiina -date: 08.10.2026 -content_type: concept -language: en ---- - -#Heading without a space after the hash - -This deliberately violates MD018 to confirm the markdownlint Action picks it up. From b48c3ec02a349f628cf9ff2ceae16ebde225bb38 Mon Sep 17 00:00:00 2001 From: Bergfrid Dias Date: Mon, 10 Aug 2026 13:40:13 +0200 Subject: [PATCH 3/5] Exclude generated/template folders from the markdownlint workflow Per direct feedback: reference/ folders, database/tables/, release-notes/api/, release-notes/database/, blueprint-templates/, and api-sources/ should not be linted by the new Action. Empirically found that markdownlint-cli2's own .markdownlintignore does not filter an explicit list of file paths (only broad, unqualified glob expansion) - confirmed by testing existing entries like /downloads/ against a direct path. So the actual exclusion is implemented via tj-actions/changed-files' files_ignore input, which filters before markdownlint-cli2 ever sees the paths. .markdownlintignore is still updated to match, for local/full-tree linting. Two temporary test files confirm both a literal-path exclusion (blueprint-templates/) and the general reference/** wildcard; will remove once confirmed on this PR. --- .github/workflows/markdownlint.yml | 8 ++++++++ .markdownlintignore | 8 ++++---- blueprint-templates/zz-ci-test-delete-me.md | 3 +++ en/api/reference/zz-ci-test-delete-me.md | 3 +++ 4 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 blueprint-templates/zz-ci-test-delete-me.md create mode 100644 en/api/reference/zz-ci-test-delete-me.md diff --git a/.github/workflows/markdownlint.yml b/.github/workflows/markdownlint.yml index 1e7f12fe46..9b02f77d4e 100644 --- a/.github/workflows/markdownlint.yml +++ b/.github/workflows/markdownlint.yml @@ -31,6 +31,14 @@ jobs: files: | **/*.md **/*.mdx + files_ignore: | + **/reference/** + en/database/tables/** + release-notes/api/** + release-notes/database/** + blueprint-templates/** + api-sources/** + downloads/** - name: Set up Node if: steps.changed.outputs.any_changed == 'true' diff --git a/.markdownlintignore b/.markdownlintignore index cefcb6866c..6cf5acfc0e 100644 --- a/.markdownlintignore +++ b/.markdownlintignore @@ -1,12 +1,12 @@ **/**/*.{cs,xml,yml} /downloads/ -/en/api/archive-providers/reference/ -/en/api/mdo-providers/reference/ -/en/api/reference/ -/en/automation/crmscript/reference/ +**/reference/ /en/database/tables/ /config/ +/release-notes/api/ /release-notes/database/ +/blueprint-templates/ +/api-sources/ /vale/ .gitignore .mintignore diff --git a/blueprint-templates/zz-ci-test-delete-me.md b/blueprint-templates/zz-ci-test-delete-me.md new file mode 100644 index 0000000000..15348c7a58 --- /dev/null +++ b/blueprint-templates/zz-ci-test-delete-me.md @@ -0,0 +1,3 @@ +#Heading without a space after the hash + +Temporary file to confirm the markdownlint Action's `files_ignore` correctly excludes `blueprint-templates/`. Removed before this PR is marked ready again. diff --git a/en/api/reference/zz-ci-test-delete-me.md b/en/api/reference/zz-ci-test-delete-me.md new file mode 100644 index 0000000000..b4a48e00c0 --- /dev/null +++ b/en/api/reference/zz-ci-test-delete-me.md @@ -0,0 +1,3 @@ +#Heading without a space after the hash + +Temporary file to confirm the markdownlint Action's `files_ignore` correctly excludes `**/reference/**`. Removed before this PR is marked ready again. From 0f1a2ddda0fec5973d007fde2ec05e4f2601d209 Mon Sep 17 00:00:00 2001 From: Bergfrid Dias Date: Mon, 10 Aug 2026 13:41:23 +0200 Subject: [PATCH 4/5] Remove files_ignore verification test files Confirmed on PR #207 that files_ignore correctly excludes both a literal path (blueprint-templates/) and the reference/** wildcard - the Action's changed-files list only included the 4 genuinely changed docs files, none of the excluded ones. --- blueprint-templates/zz-ci-test-delete-me.md | 3 --- en/api/reference/zz-ci-test-delete-me.md | 3 --- 2 files changed, 6 deletions(-) delete mode 100644 blueprint-templates/zz-ci-test-delete-me.md delete mode 100644 en/api/reference/zz-ci-test-delete-me.md diff --git a/blueprint-templates/zz-ci-test-delete-me.md b/blueprint-templates/zz-ci-test-delete-me.md deleted file mode 100644 index 15348c7a58..0000000000 --- a/blueprint-templates/zz-ci-test-delete-me.md +++ /dev/null @@ -1,3 +0,0 @@ -#Heading without a space after the hash - -Temporary file to confirm the markdownlint Action's `files_ignore` correctly excludes `blueprint-templates/`. Removed before this PR is marked ready again. diff --git a/en/api/reference/zz-ci-test-delete-me.md b/en/api/reference/zz-ci-test-delete-me.md deleted file mode 100644 index b4a48e00c0..0000000000 --- a/en/api/reference/zz-ci-test-delete-me.md +++ /dev/null @@ -1,3 +0,0 @@ -#Heading without a space after the hash - -Temporary file to confirm the markdownlint Action's `files_ignore` correctly excludes `**/reference/**`. Removed before this PR is marked ready again. From f6d896cdc084a9b6d5c9849320e4539a86686780 Mon Sep 17 00:00:00 2001 From: Bergfrid Dias Date: Tue, 11 Aug 2026 08:28:38 +0200 Subject: [PATCH 5/5] Correction round: drop redundant anchor, fix bold-colon convention deployment.mdx's explicit {#deploy-trigger-mechanics} anchor matched the auto-generated slug exactly, so it was redundant - removed. automated-tests.mdx's scheduled-check bullets used **Term**: instead of the established **Term:** convention already used elsewhere in this repo - fixed all five. --- contribute/automated-tests.mdx | 10 +++++----- contribute/deployment.mdx | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contribute/automated-tests.mdx b/contribute/automated-tests.mdx index d11569f6ac..21b6425862 100644 --- a/contribute/automated-tests.mdx +++ b/contribute/automated-tests.mdx @@ -44,11 +44,11 @@ A Mintlify community report (October 2025) describes a case where a page serving A per-PR link check only catches breaks introduced by that PR. A separate, Mintlify-hosted automation periodically re-scans the *entire* site, catching drift from any source - including direct admin-bypass pushes that skip PR review entirely. -* **Type**: `broken-link-detection` (`mint automations create --type broken-link-detection`) -* **Cadence**: `0 6 1,15 * *` - the 1st and 15th of each month, run at 06:00. Standard cron has no native "every 2 weeks" field; this is the closest non-drifting approximation of a biweekly cadence (actual gaps land at 13-16 days depending on the month). -* **Auto-merge**: off. When the automation finds and fixes a broken link, it opens a PR like any other change - it goes through the same manual review as everything else in this repo, it does not merge itself. -* **What it does**: for each broken internal link, it checks whether the target page was renamed or moved and updates the link, or - if the target was removed entirely - edits the surrounding copy to remove or replace the reference. -* **Cost**: billed in Mintlify credits per run (rough public estimate: ~285 credits/run) - a real operating cost, not a free background job. +* **Type:** `broken-link-detection` (`mint automations create --type broken-link-detection`) +* **Cadence:** `0 6 1,15 * *` - the 1st and 15th of each month, run at 06:00. Standard cron has no native "every 2 weeks" field; this is the closest non-drifting approximation of a biweekly cadence (actual gaps land at 13-16 days depending on the month). +* **Auto-merge:** off. When the automation finds and fixes a broken link, it opens a PR like any other change - it goes through the same manual review as everything else in this repo, it does not merge itself. +* **What it does:** for each broken internal link, it checks whether the target page was renamed or moved and updates the link, or - if the target was removed entirely - edits the surrounding copy to remove or replace the reference. +* **Cost:** billed in Mintlify credits per run (rough public estimate: ~285 credits/run) - a real operating cost, not a free background job. See [issue #81][4] for the setup record (creation command, confirmed `mint automations list` output). diff --git a/contribute/deployment.mdx b/contribute/deployment.mdx index f052264079..1e359e0767 100644 --- a/contribute/deployment.mdx +++ b/contribute/deployment.mdx @@ -43,7 +43,7 @@ Requested by the Mintlify GitHub App on `SuperOfficeDocs` (see [Mintlify's GitHu | `deployments` | Read/write - powers PR preview deployments | | `pull requests` | Read/write - lets the web editor create branches/PRs | -## Deploy trigger mechanics {#deploy-trigger-mechanics} +## Deploy trigger mechanics A push to the connected branch (`main`) triggers an automatic production deploy; a pull request against it gets its own preview deployment and check. A manual **Deploy** button in the dashboard exists as a fallback. See [Deployments][2] for the full mechanics - not duplicated here.