update-gh-pages-documentation.sh 8.8 KB

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