Skip to content

Instantly share code, notes, and snippets.

@SolomonHD
Last active July 21, 2025 00:42
Show Gist options
  • Save SolomonHD/ff209f45c80f1ec0068f6b39203a302d to your computer and use it in GitHub Desktop.
Save SolomonHD/ff209f45c80f1ec0068f6b39203a302d to your computer and use it in GitHub Desktop.
GitHub Pages Setup Script
#!/bin/bash
# jekyll-ghpages-setup
# Script to initialize gh-pages branch with docs folder for GitHub Pages
# Run this script from the root of your repository
#
# Documentation: https://gist.github.com/SolomonHD/1391d973b86c0aeb3e901cceeb34650f
set -euo pipefail # Exit on error, undefined vars, pipe failures
# Configuration
readonly BRANCH_NAME="gh-pages"
readonly DOCS_FOLDER="docs"
readonly REMOTE_NAME="origin"
# Colors for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly CYAN='\033[0;36m'
readonly NC='\033[0m' # No Color
# Helper functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${CYAN}[STEP]${NC} $1"
}
# Check if we're in a git repository
check_git_repo() {
if ! git rev-parse --git-dir > /dev/null 2>&1; then
log_error "Not in a git repository. Please run this script from the root of your git repository."
exit 1
fi
log_info "Git repository detected"
}
# Check if remote exists
check_remote() {
if ! git remote get-url "$REMOTE_NAME" > /dev/null 2>&1; then
log_error "Remote '$REMOTE_NAME' not found. Please ensure your repository has a remote configured."
exit 1
fi
log_info "Remote '$REMOTE_NAME' found: $(git remote get-url $REMOTE_NAME)"
}
# Check if branch exists on remote
check_remote_branch() {
log_info "Checking if '$BRANCH_NAME' branch exists on remote..."
# Fetch latest remote information
git fetch "$REMOTE_NAME" > /dev/null 2>&1 || true
if git ls-remote --heads "$REMOTE_NAME" "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then
log_info "Branch '$BRANCH_NAME' exists on remote"
return 0
else
log_info "Branch '$BRANCH_NAME' does not exist on remote"
return 1
fi
}
# Create gh-pages branch with docs folder
create_ghpages_branch() {
log_info "Creating '$BRANCH_NAME' branch with '$DOCS_FOLDER' folder..."
# Save current branch
local current_branch
current_branch=$(git branch --show-current)
log_info "Current branch: $current_branch"
# Create orphan branch (no history)
git checkout --orphan "$BRANCH_NAME"
# Remove all files from staging
git rm -rf . > /dev/null 2>&1 || true
# Create docs folder and basic structure
mkdir -p "$DOCS_FOLDER"
# Create basic index.html in docs folder
cat > "$DOCS_FOLDER/index.html" << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GitHub Pages Site</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 800px;
margin: 2rem auto;
padding: 0 1rem;
line-height: 1.6;
}
.header {
text-align: center;
padding: 2rem 0;
border-bottom: 1px solid #eee;
margin-bottom: 2rem;
}
.status {
background: #d4edda;
border: 1px solid #c3e6cb;
padding: 1rem;
border-radius: 4px;
margin: 1rem 0;
}
</style>
</head>
<body>
<div class="header">
<h1>🚀 GitHub Pages Site</h1>
<p>Successfully deployed to GitHub Pages!</p>
</div>
<div class="status">
<strong>Status:</strong> Site is live and ready for content.
</div>
<h2>Next Steps</h2>
<ul>
<li>Replace this file with your actual content</li>
<li>Configure Jekyll if using Markdown</li>
<li>Set up your CI/CD pipeline</li>
<li>Configure custom domain (if needed)</li>
</ul>
<h2>Deployment Info</h2>
<ul>
<li><strong>Branch:</strong> gh-pages</li>
<li><strong>Source:</strong> /docs folder</li>
<li><strong>Generated:</strong> <script>document.write(new Date().toISOString())</script></li>
</ul>
</body>
</html>
EOF
# Create README for the gh-pages branch
cat > README.md << 'EOF'
# GitHub Pages Branch
This branch contains the built/deployed version of the site for GitHub Pages.
## Structure
- `/docs/` - Contains the actual site files served by GitHub Pages
- This branch is automatically managed by CI/CD
## Configuration
- **Source:** `gh-pages` branch
- **Folder:** `/docs`
- **Build:** Automated via Jenkins/CI
## DO NOT EDIT MANUALLY
This branch is automatically updated by the build process.
All changes should be made to the source branch and deployed via CI/CD.
EOF
# Create .gitignore
cat > .gitignore << 'EOF'
# Logs
*.log
# Temporary files
*.tmp
*.temp
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
EOF
# Add all files
git add .
# Commit initial structure
git commit -m "Initial gh-pages setup with docs folder
- Created basic index.html in docs folder
- Added README with branch information
- Configured for GitHub Pages deployment
- Ready for CI/CD integration"
log_success "Created '$BRANCH_NAME' branch with initial structure"
}
# Configure GitHub Pages via API
configure_pages_api() {
log_step "Configuring GitHub Pages via API..."
# Get repository info from remote URL
local repo_url
repo_url=$(git remote get-url "$REMOTE_NAME")
local repo_owner repo_name api_base
# Parse repository owner and name from different URL formats
if [[ $repo_url =~ github\.com[:/]([^/]+)/([^/]+)(\.git)?$ ]]; then
repo_owner="${BASH_REMATCH[1]}"
repo_name="${BASH_REMATCH[2]%.git}"
api_base="https://api.github.com"
elif [[ $repo_url =~ ([^/:]+)[:/]([^/]+)/([^/]+)(\.git)?$ ]]; then
# For GHES instances
local ghes_host="${BASH_REMATCH[1]}"
repo_owner="${BASH_REMATCH[2]}"
repo_name="${BASH_REMATCH[3]%.git}"
api_base="https://$ghes_host/api/v3"
else
log_warning "Could not parse repository URL for API configuration"
show_manual_setup_instructions
return 1
fi
log_info "Repository: $repo_owner/$repo_name"
# Try GitHub CLI first (easier authentication)
if command -v gh &> /dev/null; then
log_info "Using GitHub CLI for API configuration..."
# Check if we're authenticated
if gh auth status &> /dev/null; then
if configure_with_gh_cli "$repo_owner" "$repo_name"; then
return 0
else
log_warning "GitHub CLI configuration failed, trying manual methods..."
fi
else
log_info "GitHub CLI not authenticated, trying manual methods..."
fi
fi
# Try with curl and environment token
if configure_with_curl "$api_base" "$repo_owner" "$repo_name"; then
return 0
fi
# Fall back to manual instructions
log_warning "Automatic API configuration not available"
show_manual_setup_instructions
return 1
}
# Configure using GitHub CLI
configure_with_gh_cli() {
local owner="$1"
local repo="$2"
# First check if Pages is already configured
if gh api "repos/$owner/$repo/pages" &> /dev/null; then
log_info "GitHub Pages already exists, updating configuration to use /docs folder..."
if gh api "repos/$owner/$repo/pages" \
--method PUT \
--field source.branch="$BRANCH_NAME" \
--field source.path="/$DOCS_FOLDER" &> /dev/null; then
log_success "GitHub Pages configuration updated to use /$DOCS_FOLDER folder via GitHub CLI"
return 0
fi
else
log_info "Creating new GitHub Pages configuration with /docs folder..."
if gh api "repos/$owner/$repo/pages" \
--method POST \
--field source.branch="$BRANCH_NAME" \
--field source.path="/$DOCS_FOLDER" &> /dev/null; then
log_success "GitHub Pages configured to use /$DOCS_FOLDER folder via GitHub CLI"
return 0
fi
fi
return 1
}
# Configure using curl with token
configure_with_curl() {
local api_base="$1"
local owner="$2"
local repo="$3"
# Check for GitHub token in environment
local token=""
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
token="$GITHUB_TOKEN"
elif [[ -n "${GH_TOKEN:-}" ]]; then
token="$GH_TOKEN"
else
log_info "No GitHub token found in environment (GITHUB_TOKEN or GH_TOKEN)"
return 1
fi
log_info "Using curl with GitHub token for API configuration..."
# Check if Pages already exists
local pages_exists=false
if curl -s -f \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $token" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"$api_base/repos/$owner/$repo/pages" > /dev/null 2>&1; then
pages_exists=true
fi
local method="POST"
local action="Creating"
if [[ "$pages_exists" == true ]]; then
method="PUT"
action="Updating"
fi
log_info "$action GitHub Pages configuration to use /$DOCS_FOLDER folder..."
# Configure Pages with API to use /docs folder
local response http_code
response=$(curl -s -w "%{http_code}" \
-X "$method" \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $token" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"$api_base/repos/$owner/$repo/pages" \
-d "{\"source\":{\"branch\":\"$BRANCH_NAME\",\"path\":\"/$DOCS_FOLDER\"}}")
http_code="${response: -3}"
if [[ "$http_code" =~ ^(200|201)$ ]]; then
log_success "GitHub Pages configured to use /$DOCS_FOLDER folder via API"
return 0
else
log_warning "API request failed with HTTP $http_code"
return 1
fi
}
# Show manual setup instructions
show_manual_setup_instructions() {
echo
log_info "Manual GitHub Pages configuration required:"
echo "1. Go to your repository Settings → Pages"
echo "2. Under 'Build and deployment' → 'Source', select 'Deploy from a branch'"
echo "3. Set Branch to: '$BRANCH_NAME'"
echo "4. Set Folder to: '/$DOCS_FOLDER' (NOT root /)"
echo "5. Click 'Save'"
echo
echo -e "${YELLOW}⚠️ Important: Make sure to select '/$DOCS_FOLDER' folder, not root (/)${NC}"
}
# Push branch to remote and configure Pages
push_and_configure() {
log_info "Pushing '$BRANCH_NAME' branch to remote..."
if git push "$REMOTE_NAME" "$BRANCH_NAME"; then
log_success "Successfully pushed '$BRANCH_NAME' branch to remote"
# Configure GitHub Pages via API
configure_pages_api
else
log_error "Failed to push '$BRANCH_NAME' branch to remote"
return 1
fi
}
# Return to original branch
return_to_original_branch() {
local current_branch
current_branch=$(git branch --show-current)
if [[ "$CURRENT_BRANCH" != "$current_branch" ]]; then
log_info "Returning to original branch: $CURRENT_BRANCH"
git checkout "$CURRENT_BRANCH"
fi
}
# Display success message with next steps
show_completion_message() {
echo
log_success "GitHub Pages setup completed successfully!"
echo
echo -e "${BLUE}🌐 Your site will be available at:${NC}"
# Get repository URL for Pages URL construction
local repo_url
repo_url=$(git remote get-url "$REMOTE_NAME")
if [[ $repo_url =~ github\.com[:/]([^/]+)/([^/]+)(\.git)?$ ]]; then
local user_org="${BASH_REMATCH[1]}"
local repo_name="${BASH_REMATCH[2]%.git}"
echo " https://$user_org.github.io/$repo_name"
elif [[ $repo_url =~ ([^/:]+)[:/]([^/]+)/([^/]+)(\.git)?$ ]]; then
# For GHES instances
local ghes_host="${BASH_REMATCH[1]}"
local user_org="${BASH_REMATCH[2]}"
local repo_name="${BASH_REMATCH[3]%.git}"
echo " https://$ghes_host/pages/$user_org/$repo_name"
fi
echo
echo -e "${BLUE}📁 Repository Structure:${NC}"
echo " • Source: '$BRANCH_NAME' branch"
echo " • Folder: '/$DOCS_FOLDER' (configured via API)"
echo " • Branch: '$CURRENT_BRANCH' (your working branch)"
echo
echo -e "${BLUE}🚀 Next Steps:${NC}"
echo "1. Your Jekyll source files go in the 'src/' directory"
echo "2. Run 'jekyll-ghpages-deploy' to build and deploy your site"
echo "3. Check repository Settings → Pages to verify /$DOCS_FOLDER is selected"
echo
echo -e "${BLUE}🔧 Authentication Methods Detected:${NC}"
if command -v gh &> /dev/null && gh auth status &> /dev/null; then
echo " ✅ GitHub CLI (gh) - authenticated"
else
echo " ❌ GitHub CLI (gh) - not available/authenticated"
fi
if [[ -n "${GITHUB_TOKEN:-}" ]] || [[ -n "${GH_TOKEN:-}" ]]; then
echo " ✅ Environment token - available"
else
echo " ❌ Environment token - not set"
fi
echo
echo -e "${BLUE}💡 For automatic API configuration in the future:${NC}"
echo " • Install GitHub CLI: https://cli.github.com/"
echo " • Or set GITHUB_TOKEN environment variable"
echo
}
# Main execution
main() {
log_info "Starting GitHub Pages setup..."
echo
# Perform checks
check_git_repo
check_remote
# Save current branch for later
CURRENT_BRANCH=$(git branch --show-current)
# Check if branch already exists
if check_remote_branch; then
log_warning "Branch '$BRANCH_NAME' already exists on remote"
echo
read -p "Do you want to recreate it? This will DELETE the existing branch! (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "Aborted by user"
exit 0
fi
log_warning "Deleting existing '$BRANCH_NAME' branch on remote..."
git push "$REMOTE_NAME" --delete "$BRANCH_NAME" || true
fi
# Create and setup branch
create_ghpages_branch
push_and_configure
return_to_original_branch
# Show completion message
show_completion_message
}
# Run main function
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment