update-gh-pages-documentation.sh 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. #!/bin/bash
  2. #
  3. ##===----------------------------------------------------------------------===##
  4. ##
  5. ## This source file is part of the OpenSwiftUI open source project
  6. ##
  7. ## Copyright (c) 2025 the OpenSwiftUI project authors
  8. ## Licensed under Apache License v2.0
  9. ##
  10. ## See LICENSE.txt for license information
  11. ##
  12. ## SPDX-License-Identifier: Apache-2.0
  13. ##
  14. ##===----------------------------------------------------------------------===##
  15. set -euo pipefail
  16. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  17. REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
  18. DOCS_DIR="$REPO_ROOT/.docs"
  19. BUILD_DIR="$DOCS_DIR/build"
  20. DOCC_OUTPUT_DIR="$BUILD_DIR/docc-output"
  21. # Default configuration
  22. BUILD_DOCS=true
  23. MINIMUM_ACCESS_LEVEL="public"
  24. TARGET_NAME="OpenSwiftUI"
  25. HOSTING_BASE_PATH="/OpenSwiftUI"
  26. CLEAN_BUILD=false
  27. SOURCE_SERVICE="github"
  28. SOURCE_SERVICE_BASE_URL="https://github.com/OpenSwiftUIProject/OpenSwiftUI/blob/main"
  29. FORCE_PUSH=true # Force push to save git repo size (avoids accumulating large binary files)
  30. ECHO_WITHOUT_PUSH=false # Echo push command without actually pushing
  31. # Colors for output
  32. RED='\033[0;31m'
  33. GREEN='\033[0;32m'
  34. YELLOW='\033[1;33m'
  35. NC='\033[0m' # No Color
  36. usage() {
  37. cat << EOF
  38. Usage: $(basename "$0") [OPTIONS]
  39. Publish Swift documentation to GitHub Pages using DocC.
  40. This legacy script will build documentation (or use existing build) and deploy
  41. it to the gh-pages branch for GitHub Pages hosting. The canonical deployment
  42. path is now .github/workflows/documentation.yml.
  43. OPTIONS:
  44. --no-build Skip building documentation (use existing output)
  45. --minimum-access-level LEVEL Set minimum access level (public, internal, private)
  46. Default: public
  47. --target TARGET Target to document (default: OpenSwiftUI)
  48. --hosting-base-path PATH Base path for hosting (default: /OpenSwiftUI)
  49. --source-service SERVICE Source service (github, gitlab, bitbucket)
  50. --source-service-base-url URL Base URL for source service
  51. (e.g., https://github.com/user/repo/blob/main)
  52. --no-force Don't force push (preserves gh-pages history)
  53. Default: force push to save repo size
  54. --clean Clean build artifacts and force rebuild
  55. --echo-without-push Echo push command without actually pushing (for testing)
  56. -h, --help Show this help message
  57. EXAMPLES:
  58. # Build and publish to GitHub Pages (source links enabled by default)
  59. $(basename "$0")
  60. # Publish using existing documentation build
  61. $(basename "$0") --no-build
  62. # Build with internal symbols and publish
  63. $(basename "$0") --minimum-access-level internal
  64. # Document a specific target
  65. $(basename "$0") --target OpenSwiftUICore
  66. # Build and publish with custom source service (e.g., for a fork)
  67. $(basename "$0") \\
  68. --source-service github \\
  69. --source-service-base-url https://github.com/yourname/OpenSwiftUI/blob/custom-branch
  70. EOF
  71. exit 0
  72. }
  73. log_info() {
  74. echo -e "${GREEN}[INFO]${NC} $1"
  75. }
  76. log_warning() {
  77. echo -e "${YELLOW}[WARNING]${NC} $1"
  78. }
  79. log_error() {
  80. echo -e "${RED}[ERROR]${NC} $1"
  81. }
  82. git_push() {
  83. if [[ "$ECHO_WITHOUT_PUSH" == true ]]; then
  84. echo "[echo without push]: git push $*"
  85. else
  86. git push "$@"
  87. fi
  88. }
  89. # Parse command line arguments
  90. while [[ $# -gt 0 ]]; do
  91. case $1 in
  92. --no-build)
  93. BUILD_DOCS=false
  94. shift
  95. ;;
  96. --minimum-access-level)
  97. MINIMUM_ACCESS_LEVEL="$2"
  98. shift 2
  99. ;;
  100. --target)
  101. TARGET_NAME="$2"
  102. shift 2
  103. ;;
  104. --hosting-base-path)
  105. HOSTING_BASE_PATH="$2"
  106. shift 2
  107. ;;
  108. --source-service)
  109. SOURCE_SERVICE="$2"
  110. shift 2
  111. ;;
  112. --source-service-base-url)
  113. SOURCE_SERVICE_BASE_URL="$2"
  114. shift 2
  115. ;;
  116. --no-force)
  117. FORCE_PUSH=false
  118. shift
  119. ;;
  120. --clean)
  121. CLEAN_BUILD=true
  122. shift
  123. ;;
  124. --echo-without-push)
  125. ECHO_WITHOUT_PUSH=true
  126. shift
  127. ;;
  128. -h|--help)
  129. usage
  130. ;;
  131. *)
  132. log_error "Unknown option: $1"
  133. usage
  134. ;;
  135. esac
  136. done
  137. log_info "Configuration:"
  138. log_info " Target: $TARGET_NAME"
  139. log_info " Build Documentation: $BUILD_DOCS"
  140. log_info " Force Push: $FORCE_PUSH"
  141. log_warning "This is the legacy manual deployment path. Documentation is deployed from CI on version tags and manual workflow runs."
  142. if [[ -n "$HOSTING_BASE_PATH" ]]; then
  143. log_info " Hosting Base Path: $HOSTING_BASE_PATH"
  144. fi
  145. # Check for required tools
  146. command -v git >/dev/null 2>&1 || {
  147. log_error "git is required but not installed. Aborting."
  148. exit 1
  149. }
  150. # Build documentation if requested
  151. if [[ "$BUILD_DOCS" == true ]]; then
  152. log_info "Building documentation..."
  153. BUILD_SCRIPT="$SCRIPT_DIR/build-documentation.sh"
  154. if [[ ! -f "$BUILD_SCRIPT" ]]; then
  155. log_error "Build script not found: $BUILD_SCRIPT"
  156. exit 1
  157. fi
  158. BUILD_ARGS=()
  159. BUILD_ARGS+=(--target "$TARGET_NAME")
  160. BUILD_ARGS+=(--minimum-access-level "$MINIMUM_ACCESS_LEVEL")
  161. if [[ -n "$HOSTING_BASE_PATH" ]]; then
  162. BUILD_ARGS+=(--hosting-base-path "$HOSTING_BASE_PATH")
  163. fi
  164. if [[ -n "$SOURCE_SERVICE" ]]; then
  165. BUILD_ARGS+=(--source-service "$SOURCE_SERVICE")
  166. BUILD_ARGS+=(--source-service-base-url "$SOURCE_SERVICE_BASE_URL")
  167. fi
  168. if [[ "$CLEAN_BUILD" == true ]]; then
  169. BUILD_ARGS+=(--clean)
  170. fi
  171. "$BUILD_SCRIPT" "${BUILD_ARGS[@]}"
  172. fi
  173. # Verify documentation output exists
  174. if [[ ! -d "$DOCC_OUTPUT_DIR" ]] || [[ -z "$(ls -A "$DOCC_OUTPUT_DIR" 2>/dev/null)" ]]; then
  175. log_error "Documentation output not found at: $DOCC_OUTPUT_DIR"
  176. log_error "Please build documentation first or run without --no-build"
  177. exit 1
  178. fi
  179. log_info "Using documentation from: $DOCC_OUTPUT_DIR"
  180. # Deploy to GitHub Pages
  181. GH_PAGES_DIR="$REPO_ROOT/gh-pages"
  182. log_info "Preparing GitHub Pages deployment..."
  183. # Check if we're in a git repository
  184. if ! git rev-parse --git-dir > /dev/null 2>&1; then
  185. log_error "Not in a git repository. Cannot deploy to GitHub Pages."
  186. exit 1
  187. fi
  188. # Store current branch
  189. CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
  190. # Check if gh-pages branch exists
  191. if git show-ref --verify --quiet refs/heads/gh-pages; then
  192. log_info "Using existing gh-pages branch"
  193. git fetch origin gh-pages 2>/dev/null || true
  194. git worktree add "$GH_PAGES_DIR" gh-pages || {
  195. log_warning "Worktree already exists, removing and recreating..."
  196. git worktree remove "$GH_PAGES_DIR" --force
  197. git worktree add "$GH_PAGES_DIR" gh-pages
  198. }
  199. else
  200. log_info "Creating new gh-pages branch"
  201. # Create an empty orphan branch first
  202. git checkout --orphan gh-pages
  203. git rm -rf . 2>/dev/null || true
  204. git commit --allow-empty -m "Initialize gh-pages branch"
  205. git checkout "$CURRENT_BRANCH"
  206. # Now create worktree from the new branch
  207. git worktree add "$GH_PAGES_DIR" gh-pages
  208. fi
  209. # Copy documentation to gh-pages worktree docs folder
  210. log_info "Copying documentation files..."
  211. mkdir -p "$GH_PAGES_DIR/docs"
  212. rsync -av --delete "$DOCC_OUTPUT_DIR/" "$GH_PAGES_DIR/docs/"
  213. # Add .nojekyll to prevent GitHub Pages from processing with Jekyll
  214. touch "$GH_PAGES_DIR/.nojekyll"
  215. # Commit and push
  216. cd "$GH_PAGES_DIR"
  217. git add -Af .
  218. if git diff --cached --quiet; then
  219. log_info "No changes to documentation"
  220. else
  221. log_info "Committing documentation changes..."
  222. if [[ "$FORCE_PUSH" == true ]]; then
  223. # Use --amend to avoid accumulating commits with large binary files
  224. # Check if there are any commits in the branch
  225. if git rev-parse HEAD >/dev/null 2>&1; then
  226. git commit --amend -m "Update documentation (generated from $CURRENT_BRANCH@$(git -C "$REPO_ROOT" rev-parse --short HEAD))"
  227. else
  228. # First commit on the branch
  229. git commit -m "Update documentation (generated from $CURRENT_BRANCH@$(git -C "$REPO_ROOT" rev-parse --short HEAD))"
  230. fi
  231. log_info "Pushing to gh-pages branch..."
  232. log_info "Using --force to save git repo size (avoids accumulating large binary files)"
  233. git_push --force origin gh-pages
  234. else
  235. git commit -m "Update documentation (generated from $CURRENT_BRANCH@$(git -C "$REPO_ROOT" rev-parse --short HEAD))"
  236. log_info "Pushing to gh-pages branch..."
  237. git_push origin gh-pages
  238. fi
  239. log_info "${GREEN}✓${NC} Documentation published successfully!"
  240. log_info "GitHub Pages will be updated shortly at your repository's GitHub Pages URL"
  241. fi
  242. # Cleanup
  243. cd "$REPO_ROOT"
  244. git worktree remove "$GH_PAGES_DIR"
  245. log_info "Cleaning up build artifacts..."
  246. rm -rf "$DOCS_DIR"
  247. log_info "Done!"