diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 35e4cf34b..87d7b918f 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -3,22 +3,55 @@ name: GH-Pages on: workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: - build: + versions: runs-on: ubuntu-latest + outputs: + targets: ${{ steps.list.outputs.targets }} + latest: ${{ steps.list.outputs.latest }} + steps: + - uses: actions/checkout@v7 + - id: list + run: ./publish.sh list >> "$GITHUB_OUTPUT" - concurrency: - group: ${{ github.ref }} - cancel-in-progress: true - + build: + needs: versions + runs-on: ubuntu-latest + strategy: + # One broken version should not prevent the others from being published. + fail-fast: false + matrix: + version: ${{ fromJson(needs.versions.outputs.targets) }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - uses: subosito/flutter-action@v2 - uses: bluefireteam/melos-action@v3 with: - melos-version: 7.0.0-dev.7 + melos-version: 8.2.2 run-bootstrap: false - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v7 + with: + node-version: 22 + - run: ./publish.sh build "${{ matrix.version }}" "${{ needs.versions.outputs.latest }}" + - uses: actions/upload-artifact@v7 + with: + name: docs-${{ matrix.version }} + path: out/ + retention-days: 1 + + publish: + needs: [versions, build] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v7 + - uses: actions/download-artifact@v8 with: - node-version: 16 - - run: ./publish.sh + path: artifacts + pattern: docs-* + - run: ./publish.sh assemble '${{ needs.versions.outputs.targets }}' diff --git a/publish.sh b/publish.sh index e9e140721..4c1c22734 100755 --- a/publish.sh +++ b/publish.sh @@ -1,48 +1,97 @@ #!/bin/bash -e +# +# Builds the versioned Flame documentation. +# +# Usage: +# ./publish.sh Build every version sequentially, then +# assemble docs/ and push (local/full run). +# ./publish.sh list Print `latest` and `targets` (a JSON array +# of everything to build, in site order). +# ./publish.sh build Build a single version into out/. +# ./publish.sh assemble Combine out/ (or artifacts/) into docs/ +# and push. +# +# The CI workflow uses `list` to fan out one `build` job per version, then a +# single `assemble` job to publish. Splitting it this way keeps one copy of the +# build logic rather than duplicating it between the script and the workflow. tmp_flame_src='_flame' tmp_stash='_stash' +flame_repo='https://github.com/flame-engine/flame.git' +out_dir='out' function main { + case "${1:-}" in + list) cmd_list ;; + build) cmd_build "$2" "$3" ;; + assemble) cmd_assemble "$2" ;; + '') cmd_all ;; + *) echo "Unknown command: $1" >&2; exit 1 ;; + esac +} + +function section { + echo + echo -e "\033[1;32m#-----------------------------------------------------------\033[m" + echo -e "\033[1;32m# $1\033[m" + echo -e "\033[1;32m#-----------------------------------------------------------\033[m" +} + +# Obtain the list of documentation versions to build. The list is created from +# git tags, skipping the versions that started with 0, and removing all the old +# versions that don't support Melos 7 and pub workspaces. +function version_list { + git -C $tmp_flame_src for-each-ref --sort=creatordate \ + --format '%(refname:short)' 'refs/tags/v*' | sed -n '29,$p' | sort -rV +} + +# Prints `latest` and `targets` as `key=value` lines, ready to be appended to +# $GITHUB_OUTPUT. `targets` is main followed by the versions in descending +# order, which is also the order they are listed in on the site. +function cmd_list { + rm -rf $tmp_flame_src + # Only the refs are needed here, so skip blobs and the working copy. + git clone --filter=blob:none --no-checkout --quiet $flame_repo $tmp_flame_src + local list latest targets + list=$(version_list) + latest=$(head -n 1 <<< "$list") + targets=$(printf 'main\n%s\n' "$list" | jq -R . | jq -s -c .) + rm -rf $tmp_flame_src + echo "latest=$latest" + echo "targets=$targets" +} + +function cmd_build { prepare_flame_repo - prepare_docs + generate_docs_for_version "$1" "$2" +} - # Obtain the list of documentation versions to build. The list is - # created from git tags, skipping the versions that started with 0. +function cmd_all { + local list latest targets + prepare_flame_repo section "List of versions to build:" - cd $tmp_flame_src - # Removes all the old versions that doesn't support Melos 7 and pub workspaces. - list=$(git for-each-ref --sort=creatordate --format '%(refname:short)' 'refs/tags/v*' | sed -n '29,$p' | sort -rV) - cd - + list=$(version_list) echo "$list" - latest_version=$(head -n 1 <<< "$list") - sed -i "s/FLAME_VERSION/latest/g" docs/index.html - sed -i "s/FLAME_VERSION/latest/g" docs/404.html + latest=$(head -n 1 <<< "$list") + targets=$(printf 'main\n%s\n' "$list" | jq -R . | jq -s -c .) - generate_docs_for_version main "$latest_version" + generate_docs_for_version main "$latest" while IFS= read -r line; do - generate_docs_for_version "$line" "$latest_version" + generate_docs_for_version "$line" "$latest" done <<< "$list" - git_push + cmd_assemble "$targets" rm -rf $tmp_flame_src rm -rf $tmp_stash section "Done." } -function section { - echo - echo -e "\033[1;32m#-----------------------------------------------------------\033[m" - echo -e "\033[1;32m# $1\033[m" - echo -e "\033[1;32m#-----------------------------------------------------------\033[m" -} - function prepare_flame_repo { section 'Downloading Flame repository' rm -rf $tmp_flame_src rm -rf $tmp_stash - git clone https://github.com/flame-engine/flame.git $tmp_flame_src + git clone $flame_repo $tmp_flame_src mkdir $tmp_stash cp -r $tmp_flame_src/doc/_sphinx $tmp_stash cp -r $tmp_flame_src/scripts $tmp_stash @@ -56,6 +105,9 @@ function prepare_docs { cp -r template docs/ } +# Builds one version into out/. The latest version is additionally +# copied to out/latest, before the noindex marker is added, so that only the +# versioned copy is hidden from search engines. function generate_docs_for_version { version=$1 latest_version=$2 @@ -64,6 +116,14 @@ function generate_docs_for_version { cd $tmp_flame_src git checkout -f "$version" + # `git checkout -f` leaves ignored files alone, so build output generated for + # a previously built version survives into this one. That breaks the Flutter + # apps: `.dart_tool/` holds a generated web plugin registrant, and a + # registrant left behind by a version using rive ^0.14 imports + # `package:rive_native`, which versions using rive ^0.13 do not depend on. + # The app then fails to compile with "Couldn't resolve the package + # 'rive_native'". Every version has to start from a pristine checkout. + git clean -xfd cd - rm -rf $tmp_flame_src/doc/_sphinx cp -r $tmp_stash/_sphinx $tmp_flame_src/doc/ @@ -90,15 +150,17 @@ function generate_docs_for_version { make html cd - + mkdir -p $out_dir if [[ "$version" == "$latest_version" ]]; then - cp -r $tmp_flame_src/doc/_build/html "docs/latest" + rm -rf "$out_dir/latest" + cp -r $tmp_flame_src/doc/_build/html "$out_dir/latest" fi - - cp -r $tmp_flame_src/doc/_build/html "docs/$version" - no_index_string="" - find "docs/$version" -type f -name "*.html" -exec sed -i "//a $no_index_string" {} + - echo "$version" >> docs/versions.txt + rm -rf "${out_dir:?}/$version" + cp -r $tmp_flame_src/doc/_build/html "$out_dir/$version" + no_index_string="" + find "$out_dir/$version" -type f -name "*.html" \ + -exec sed -i "//a $no_index_string" {} + } function pre_process { @@ -115,6 +177,31 @@ function pre_process { fi } +# Combines the per-version builds into docs/ and publishes them. Version +# directories are read from artifacts/*/ (one directory per CI artifact) when +# present, and from out/ otherwise. +function cmd_assemble { + local targets="$1" + section "Assembling docs" + prepare_docs + sed -i "s/FLAME_VERSION/latest/g" docs/index.html + sed -i "s/FLAME_VERSION/latest/g" docs/404.html + + if compgen -G 'artifacts/*/' > /dev/null; then + cp -r artifacts/*/. docs/ + else + cp -r $out_dir/. docs/ + fi + + # versions.txt drives the version picker, so the order has to be stable: + # main first, then newest to oldest. + jq -r '.[]' <<< "$targets" > docs/versions.txt + echo "Published versions:" + cat docs/versions.txt + + git_push +} + function git_push { section "Publishing to Git" git config user.email "contact@blue-fire.xyz" @@ -128,4 +215,4 @@ function git_push { fi } -main +main "$@"