Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 7 additions & 19 deletions .github/workflows/vortex-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ jobs:

env:
CURRENT_MAJOR: ${{ vars.VORTEX_CURRENT_MAJOR || '1' }}
OTHER_MAJOR: ${{ (vars.VORTEX_CURRENT_MAJOR || '1') == '1' && '2' || '1' }}

steps:
- name: Checkout code
Expand Down Expand Up @@ -213,23 +212,12 @@ jobs:
run: yarn install --frozen-lockfile
working-directory: .vortex/docs

# Assemble the multi-version site for production: snapshot this ref's docs
# as the current major (served at the bare '/docs') and pull the other
# major's '{N}.x' branch docs in at '/docs/v{other}'. All driven by
# 'VORTEX_CURRENT_MAJOR'. Mirrors the same step in 'vortex-test-docs.yml'
# (development / Netlify).
- name: Assemble versioned docs
run: |
yarn docusaurus docs:version "${CURRENT_MAJOR}.x"
git -C "${{ github.workspace }}" fetch origin "${OTHER_MAJOR}.x" --depth=1 || { echo "Failed to fetch the ${OTHER_MAJOR}.x branch."; exit 1; }
git -C "${{ github.workspace }}" rev-parse --verify "origin/${OTHER_MAJOR}.x" || { echo "The ${OTHER_MAJOR}.x branch does not exist."; exit 1; }
rm -rf content
git -C "${{ github.workspace }}" checkout "origin/${OTHER_MAJOR}.x" -- .vortex/docs/content || { echo "Failed to check out content from ${OTHER_MAJOR}.x."; exit 1; }
working-directory: .vortex/docs

- name: Build documentation site
run: yarn run build
working-directory: .vortex/docs
# Production (GitHub Pages) serves the same combined site that
# 'vortex-test-docs.yml' builds for development deploys and previews. The
# binaries copied above are picked up from 'docs/static'.
- name: Build the combined documentation site
run: ./docs/.utils/assemble-combined-docs.sh
working-directory: .vortex
env:
VORTEX_CURRENT_MAJOR: ${{ env.CURRENT_MAJOR }}

Expand All @@ -250,7 +238,7 @@ jobs:
- name: Upload documentation site
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
with:
path: .vortex/docs/build
path: .vortex/docs_combined/build

- name: Deploy to GitHub Pages
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
32 changes: 12 additions & 20 deletions .github/workflows/vortex-test-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ jobs:
env:
CURRENT_MAJOR: ${{ vars.VORTEX_CURRENT_MAJOR || '1' }}
OTHER_MAJOR: ${{ (vars.VORTEX_CURRENT_MAJOR || '1') == '1' && '2' || '1' }}
# The major the triggering commit ships. Each major builds its own binary
# from its own workflow, so the workflow name identifies the major for
# pushes, branches and fork pull requests alike.
DOCS_MAJOR: ${{ github.event.workflow_run.name == 'Vortex - Test CLI' && '2' || '1' }}
BINARY_ARTIFACT: ${{ github.event.workflow_run.name == 'Vortex - Test CLI' && 'vortex-cli' || 'vortex-installer' }}
BINARY_FILE: ${{ github.event.workflow_run.name == 'Vortex - Test CLI' && 'vortex.phar' || 'installer.phar' }}

Expand Down Expand Up @@ -99,22 +103,6 @@ jobs:
run: yarn run spellcheck
working-directory: '${{ github.workspace }}/.vortex/docs'

# On the main (development) deploy, assemble the multi-version site:
# snapshot main's docs as the current major (served at the bare '/docs')
# and pull the other major's '{N}.x' branch docs in at '/docs/v{other}'.
# Driven by 'VORTEX_CURRENT_MAJOR'. Skipped on PR/branch builds, which stay
# single-version previews. Mirrors the same step in 'vortex-release.yml'
# (production / GitHub Pages).
- name: Assemble versioned docs
if: github.event.workflow_run.head_branch == 'main'
run: |
yarn docusaurus docs:version "${CURRENT_MAJOR}.x"
git -C "${{ github.workspace }}" fetch origin "${OTHER_MAJOR}.x" --depth=1 || { echo "Failed to fetch the ${OTHER_MAJOR}.x branch."; exit 1; }
git -C "${{ github.workspace }}" rev-parse --verify "origin/${OTHER_MAJOR}.x" || { echo "The ${OTHER_MAJOR}.x branch does not exist."; exit 1; }
rm -rf content
git -C "${{ github.workspace }}" checkout "origin/${OTHER_MAJOR}.x" -- .vortex/docs/content || { echo "Failed to check out content from ${OTHER_MAJOR}.x."; exit 1; }
working-directory: '${{ github.workspace }}/.vortex/docs'

# On the main deploy, publish both majors to match the multi-version
# docs: '/v1/install' and '/v2/install' are the stable per-major pins and
# the bare '/install' is a copy of the current major's pin (the
Expand Down Expand Up @@ -146,11 +134,15 @@ jobs:
cp "vortex-v${OTHER_MAJOR}/.vortex/cli/.build/vortex.phar" ".vortex/docs/static/v${OTHER_MAJOR}/install"
cp ".vortex/docs/static/v${CURRENT_MAJOR}/install" .vortex/docs/static/install

- name: Build documentation site
run: yarn run build
working-directory: '${{ github.workspace }}/.vortex/docs'
# Building the combined site is what checks links across both majors, so
# every branch builds it. It runs after the binaries are staged, which the
# assembly copies across with the rest of 'docs/'.
- name: Build the combined documentation site
run: ./docs/.utils/assemble-combined-docs.sh
working-directory: '${{ github.workspace }}/.vortex'
Comment thread
coderabbitai[bot] marked this conversation as resolved.
env:
VORTEX_CURRENT_MAJOR: ${{ env.CURRENT_MAJOR }}
VORTEX_DOCS_MAJOR: ${{ env.DOCS_MAJOR }}

# This workflow runs via 'workflow_run', where 'github.ref' is always the
# default branch, so the action's branch-based production detection cannot
Expand All @@ -163,7 +155,7 @@ jobs:
id: netlify
uses: nwtgck/actions-netlify@d22a32a27c918fe470bbc562e984f80ec48c2668 # v4.0
with:
publish-dir: '.vortex/docs/build'
publish-dir: '.vortex/docs_combined/build'
production-deploy: ${{ github.event.workflow_run.head_branch == 'main' }}
deploy-message: "Deploy from GitHub Actions"
enable-pull-request-comment: false
Expand Down
14 changes: 13 additions & 1 deletion .vortex/.ahoy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ commands:
[ ! -d ./docs/node_modules ] && yarn --cwd=docs install --frozen-lockfile
yarn --cwd=docs run build

docs-combined:
usage: Build the combined site serving both majors into the disposable 'docs_combined' directory.
cmd: ./docs/.utils/assemble-combined-docs.sh

docs-combined-serve:
usage: Serve the built combined site on port 4000. Pass a port as the first argument to override.
cmd: |
[ -d ./docs_combined/build ] || { echo "No combined site found. Run 'ahoy docs-combined' first."; exit 1; }
port="${1:-4000}"
echo "Serving the combined documentation site at http://localhost:${port}"
php -S "localhost:${port}" -t ./docs_combined/build ./docs/.utils/serve-router.php

build-installer:
name: Build documentation.
cmd: |
Expand Down Expand Up @@ -138,7 +150,7 @@ commands:
cmd: yarn --cwd=docs run update-variables

update-videos:
usage: Update documentation videos. Pass names to record a subset (installer, build, provision, lint, test, test-bdd); default is all six.
usage: Update documentation videos. Pass names to record a subset (installer, build, provision, lint, test, test-bdd, info, doctor, doctor-info); default is all of them. Pass --verify to re-render the committed artefacts and compare instead of recording.
cmd: php docs/.utils/update-videos.php "$@"

entrypoint:
Expand Down
4 changes: 4 additions & 0 deletions .vortex/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# The combined documentation site built by `ahoy docs-combined`. It is a
# generated copy of `docs/` carrying both majors, rebuilt from scratch on every
# run and safe to delete at any time.
/docs_combined
6 changes: 3 additions & 3 deletions .vortex/docs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
.docusaurus-dev
.cache-loader

# Assembled docs versions - created by the CI publish step (or a local
# `docusaurus docs:version` to preview the multi-version site) and never
# committed; the build auto-detects `versioned_docs/` to switch to multi-version.
# Version snapshots. Only the disposable `docs_combined/` site carries them, so
# these never appear here in normal use; the pattern stays as a guard against a
# stray local `docusaurus docs:version` run being committed.
/versioned_docs
/versioned_sidebars
/versions.json
Expand Down
165 changes: 165 additions & 0 deletions .vortex/docs/.utils/assemble-combined-docs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#!/usr/bin/env bash
##
# Assemble the combined documentation site, serving both majors from one build.
#
# Produces a complete, self-contained Docusaurus site in 'docs_combined/'. The
# current major is served at '/docs' with its assets at '/img'; the other major
# is served at '/docs/v{other}' with its assets at '/v{other}/img'.
#
# This checkout supplies the documentation for the major it ships
# ('VORTEX_DOCS_MAJOR'); the other major is read from its own branch. So a
# branch off 'main' is built as the current major and '{other}.x' is fetched,
# while a branch off '{other}.x' is built as the other major and 'main' is
# fetched. Everything the assembly writes stays inside 'docs_combined/', which
# is disposable - 'docs/' is only ever read.
#
# @usage
# cd .vortex && ./docs/.utils/assemble-combined-docs.sh

set -eu
set -o pipefail
[ "${VORTEX_DEBUG-}" = "1" ] && set -x

# The major served as the site's default version at the bare '/docs'.
VORTEX_CURRENT_MAJOR="${VORTEX_CURRENT_MAJOR:-1}"

# The major this checkout ships. Defaults to the current major, which is the
# major that lives on the default branch.
VORTEX_DOCS_MAJOR="${VORTEX_DOCS_MAJOR:-${VORTEX_CURRENT_MAJOR}}"

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../" && pwd)"
DOCS_DIR="${ROOT_DIR}/.vortex/docs"
COMBINED_DIR="${ROOT_DIR}/.vortex/docs_combined"

sed_opts=(-i) && [ "$(uname)" = "Darwin" ] && sed_opts=(-i '')

for major in "${VORTEX_CURRENT_MAJOR}" "${VORTEX_DOCS_MAJOR}"; do
case "${major}" in
1 | 2) ;;
*)
echo "ERROR: Invalid major '${major}'. Expected 1 or 2." >&2
exit 1
;;
esac
done

case "${VORTEX_CURRENT_MAJOR}" in
1) other_major=2 ;;
2) other_major=1 ;;
esac

# The current major lives on the default branch; every other major lives on its
# own '{N}.x' branch. Whichever side this checkout does not supply is fetched.
if [ "${VORTEX_DOCS_MAJOR}" = "${VORTEX_CURRENT_MAJOR}" ]; then
default_ref=""
other_ref="origin/${other_major}.x"
else
default_ref="origin/main"
other_ref=""
fi

fetch_ref="${default_ref}${other_ref}"
fetch_branch="${fetch_ref#origin/}"

git -C "${ROOT_DIR}" fetch origin "${fetch_branch}" --depth=1 || {
echo "ERROR: Failed to fetch the ${fetch_branch} branch." >&2
exit 1
}

git -C "${ROOT_DIR}" rev-parse --verify "${fetch_ref}" >/dev/null || {
echo "ERROR: The ${fetch_branch} branch does not exist." >&2
exit 1
}

##
# Replace a directory with a major's documentation tree.
#
# $1 - directory to populate.
# $2 - subdirectory of the documentation to read ('content' or 'static').
# $3 - git ref to read from, or empty to read this checkout.
##
populate() {
local dest="${1}"
local tree="${2}"
local ref="${3}"

rm -rf "${dest}"
mkdir -p "${dest}"

if [ -z "${ref}" ]; then
cp -R "${DOCS_DIR}/${tree}/." "${dest}/"
else
git -C "${ROOT_DIR}" archive "${ref}:.vortex/docs/${tree}" | tar -x -C "${dest}"
fi

[ -n "$(ls -A "${dest}")" ] || {
echo "ERROR: No '${tree}' found in ${ref:-this checkout}." >&2
exit 1
}
}

rm -rf "${COMBINED_DIR}"
mkdir -p "${COMBINED_DIR}"

# The combined site is a copy of this one, so it builds with the same config,
# components and sidebars. Generated and installed directories are excluded and
# rebuilt inside it, so it depends on nothing outside itself.
rsync -a \
--exclude '/node_modules' \
--exclude '/build' \
--exclude '/.docusaurus' \
--exclude '/.docusaurus-dev' \
--exclude '/.logs' \
"${DOCS_DIR}/" "${COMBINED_DIR}/"

yarn --cwd="${COMBINED_DIR}" install --frozen-lockfile

# Snapshot the current major, whose assets stay at the bare static root.
populate "${COMBINED_DIR}/content" content "${default_ref}"
populate "${COMBINED_DIR}/static" static "${default_ref}"

# 'VORTEX_DOCS_COMBINED' stays unset here: the snapshot the combined config
# expects does not exist until this command creates it.
yarn --cwd="${COMBINED_DIR}" docusaurus docs:version "${VORTEX_CURRENT_MAJOR}.x"

# Hand 'content/' over to the other major. Both majors record their own demo
# videos and diagrams under the same 'static/img' names, so the other major's
# assets get their own '/v{other}' prefix instead of losing to the current
# major's copies.
other_static_dir="${COMBINED_DIR}/static/v${other_major}"

# The other major's binary is built into 'static/v{other}' before the assembly
# runs and is not tracked on any branch, so replacing that directory with the
# branch's own static tree would discard it.
staged_other_install="${DOCS_DIR}/static/v${other_major}/install"
[ -f "${staged_other_install}" ] || staged_other_install=""

populate "${COMBINED_DIR}/content" content "${other_ref}"
populate "${other_static_dir}" static "${other_ref}"
Comment thread
coderabbitai[bot] marked this conversation as resolved.

[ -z "${staged_other_install}" ] || cp "${staged_other_install}" "${other_static_dir}/install"

# Every branch authors its docs against the bare '/docs' mount, so an absolute
# link lands on the current major once the content is served at '/docs/v{other}'.
# Re-point those links at the major they were written for. A link that already
# names a version is left as authored, so the alternation skips '/v' followed by
# a digit.
find "${COMBINED_DIR}/content" -type f \( -name '*.md' -o -name '*.mdx' \) -exec \
sed -E "${sed_opts[@]}" "s%\]\(/docs([)#?]|/[^v]|/v[^0-9])%](/docs/v${other_major}\1%g" {} +

# Asset references are written against the bare static root for the same reason.
# Each directory the other major ships is remapped, so a new asset directory on
# that branch is carried over without editing this script. Markdown targets and
# JSX attribute values are the two forms an asset path appears in.
for asset_dir in "${other_static_dir}"/*/; do
[ -d "${asset_dir}" ] || continue

asset_name="$(basename "${asset_dir}")"

find "${COMBINED_DIR}/content" -type f \( -name '*.md' -o -name '*.mdx' \) -exec \
sed -E "${sed_opts[@]}" "s%([\"'(])/${asset_name}/%\1/v${other_major}/${asset_name}/%g" {} +
done

VORTEX_DOCS_COMBINED=1 VORTEX_CURRENT_MAJOR="${VORTEX_CURRENT_MAJOR}" yarn --cwd="${COMBINED_DIR}" run build

echo "Combined documentation site built at ${COMBINED_DIR}/build"
55 changes: 55 additions & 0 deletions .vortex/docs/.utils/serve-router.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

/**
* @file
* Router for the PHP built-in server serving a built Docusaurus site.
*
* Docusaurus emits a directory per route, so a request without a trailing
* slash has to resolve to that directory's 'index.html'. The built-in server
* only does so for a trailing slash, and returns its own 404 page rather than
* the site's.
*
* @usage
* php -S localhost:8000 -t <build-dir> serve-router.php
*/

declare(strict_types=1);

$doc_root = $_SERVER['DOCUMENT_ROOT'];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$path = is_string($path) ? rawurldecode($path) : '/';

// Reject traversal outside the build directory before touching the filesystem.
if (str_contains($path, '..')) {
http_response_code(400);

return TRUE;
}

$target = $doc_root . $path;

// Serve an existing file as-is and let the server set the content type.
if (is_file($target)) {
return FALSE;
}

$index = rtrim($target, '/') . '/index.html';

if (is_file($index)) {
header('Content-Type: text/html; charset=UTF-8');
readfile($index);

return TRUE;
}

// Fall back to the site's own 404 page so navigation errors look like the site.
$not_found = $doc_root . '/404.html';

http_response_code(404);

if (is_file($not_found)) {
header('Content-Type: text/html; charset=UTF-8');
readfile($not_found);
}

return TRUE;
Loading