From ead6aa5bb1865af63471ca45007c350a398176e3 Mon Sep 17 00:00:00 2001 From: Shravan Goswami Date: Wed, 5 Feb 2025 23:49:57 +0530 Subject: [PATCH 1/3] Replacing bash script with Julia script for inserting navbar in Documenter sites --- DocsDocumenter/action.yml | 1 + DocsNav/action.yml | 5 +- DocsNav/scripts/insert_navbar.jl | 165 +++++++++++++++++++++++++++++++ DocsNav/scripts/insert_navbar.sh | 98 ------------------ 4 files changed, 168 insertions(+), 101 deletions(-) create mode 100644 DocsNav/scripts/insert_navbar.jl delete mode 100644 DocsNav/scripts/insert_navbar.sh diff --git a/DocsDocumenter/action.yml b/DocsDocumenter/action.yml index 9b2588c..1b4268e 100644 --- a/DocsDocumenter/action.yml +++ b/DocsDocumenter/action.yml @@ -42,6 +42,7 @@ runs: shell: julia --color=yes --project=${{ inputs.doc-path }} {0} run: | using Pkg + Pkg.add("HTTP") Pkg.develop(PackageSpec(path=pwd())) Pkg.instantiate() diff --git a/DocsNav/action.yml b/DocsNav/action.yml index a18f717..d25c9f3 100644 --- a/DocsNav/action.yml +++ b/DocsNav/action.yml @@ -5,7 +5,7 @@ inputs: doc-path: description: 'Path to the Documenter.jl output' required: false - # Most julia projects have it here + # Most Julia projects have it here default: 'docs/build' navbar-url: description: 'URL or local path of the navbar HTML to be inserted.' @@ -22,6 +22,5 @@ runs: - name: Update Navbar working-directory: ${{ inputs.doc-path }} run: | - chmod +x ${{ github.action_path }}/scripts/insert_navbar.sh - ${{ github.action_path }}/scripts/insert_navbar.sh . "${{ inputs.navbar-url }}" --exclude "${{ inputs.exclude-paths }}" + julia ${{ github.action_path }}/scripts/insert_navbar.jl . "${{ inputs.navbar-url }}" --exclude "${{ inputs.exclude-paths }}" shell: bash diff --git a/DocsNav/scripts/insert_navbar.jl b/DocsNav/scripts/insert_navbar.jl new file mode 100644 index 0000000..6f8233d --- /dev/null +++ b/DocsNav/scripts/insert_navbar.jl @@ -0,0 +1,165 @@ +# insert_navbar.jl +# +# Usage: +# julia insert_navbar.jl [--exclude "path1,path2,..."] +# +# Features: +# - Processes all .html files in the target (either a single file or recursively in a directory). +# - Removes any previously inserted navbar block (i.e. everything between and ) +# along with any extra whitespace immediately following it. +# - Inserts the new navbar block immediately after the first tag. +# - If the fetched navbar content does not contain the markers, it is wrapped (without further modification) with: +# +# (navbar content) +# +# - If a file does not contain a tag, that file is skipped. +# - An optional --exclude parameter (comma‑separated) will skip any file whose path contains one of the provided substrings. +# +# Required package: HTTP +# +# (Install HTTP via Julia’s package manager, e.g. using Pkg; Pkg.add("HTTP")) + +using HTTP + +# --- Utility: Read file contents --- +function read_file(filename::String) + open(filename, "r") do io + read(io, String) + end +end + +# --- Utility: Write contents to a file --- +function write_file(filename::String, contents::String) + open(filename, "w") do io + write(io, contents) + end +end + +# --- Exclusion Function --- +function should_exclude(filename::String, patterns::Vector{String}) + for pat in patterns + if occursin(pat, filename) + return true + end + end + return false +end + +# --- Remove any existing navbar block and any whitespace following it --- +function remove_existing_navbar(html::String) + start_marker = "" + end_marker = "" + while occursin(start_marker, html) && occursin(end_marker, html) + start_idx_range = findfirst(start_marker, html) + end_idx_range = findfirst(end_marker, html) + # Extract the first index from the returned range. + start_idx = first(start_idx_range) + end_idx = first(end_idx_range) + # Get prefix: everything before the start marker (or empty if start_idx is 1) + prefix = start_idx > 1 ? html[1:start_idx-1] : "" + # Suffix: from the end of the end marker to the end of the file, + # then remove any leading whitespace. + suffix = lstrip(html[end_idx + length(end_marker) : end]) + html = string(prefix, suffix) + end + return html +end + +# --- Wrap navbar HTML with markers if not already present --- +function wrap_navbar(navbar_html::String) + if !occursin("NAVBAR START", navbar_html) || !occursin("NAVBAR END", navbar_html) + return "\n" * navbar_html * "\n" + else + return navbar_html + end +end + +# --- Insert new navbar into HTML --- +function insert_navbar(html::String, navbar_html::String) + # Remove any previously inserted navbar block (and any trailing whitespace). + html = remove_existing_navbar(html) + # Use a regex to find the first tag (case-insensitive). + m = match(r"(?i)(]*>)", html) + if m === nothing + println("Warning: Could not find tag in the file; skipping insertion.") + return html # Return the unmodified HTML. + end + prefix = m.match # The matched tag. + # Build the inserted string: the tag, a newline, the navbar block, and a newline. + inserted = string(prefix, "\n", navbar_html, "\n") + # Replace only the first occurrence of the tag with our new content. + html = replace(html, prefix => inserted; count = 1) + return html +end + +# --- Process a Single HTML File --- +function process_file(filename::String, navbar_html::String) + println("Processing: $filename") + html = read_file(filename) + html_new = insert_navbar(html, navbar_html) + # If the HTML was not modified because no tag was found, print a message and skip writing. + if html_new == html + println("Skipped: No tag found in $filename") + else + write_file(filename, html_new) + println("Updated: $filename") + end +end + +# --- Main Function --- +function main() + if length(ARGS) < 2 + println("Usage: julia insert_navbar.jl [--exclude \"pat1,pat2,...\"]") + return + end + target = ARGS[1] + navbar_source = ARGS[2] + + # Process optional --exclude argument. + exclude_patterns = String[] + if length(ARGS) ≥ 4 && ARGS[3] == "--exclude" + exclude_patterns = map(x -> string(strip(x)), split(ARGS[4], ',')) + end + + # --- Get Navbar Content --- + navbar_html = "" + if startswith(lowercase(navbar_source), "http") + resp = HTTP.get(navbar_source) + if resp.status != 200 + error("Failed to download navbar from $navbar_source") + end + navbar_html = String(resp.body) + else + navbar_html = read_file(navbar_source) + end + # Preserve the fetched navbar content exactly; do not modify internal formatting. + navbar_html = string(navbar_html) + # Wrap with markers if not already present. + navbar_html = wrap_navbar(navbar_html) + + # --- Process Files --- + if isfile(target) + if !should_exclude(target, exclude_patterns) + process_file(target, navbar_html) + else + println("Skipping excluded file: $target") + end + elseif isdir(target) + for (root, _, files) in walkdir(target) + for file in files + if endswith(file, ".html") + fullpath = joinpath(root, file) + if !should_exclude(fullpath, exclude_patterns) + process_file(fullpath, navbar_html) + else + println("Skipping excluded file: $fullpath") + end + end + end + end + else + error("Target $target is neither a file nor a directory.") + end +end + +main() diff --git a/DocsNav/scripts/insert_navbar.sh b/DocsNav/scripts/insert_navbar.sh deleted file mode 100644 index 675b98e..0000000 --- a/DocsNav/scripts/insert_navbar.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash -# This script inserts a top navigation bar into Documenter.jl-generated sites. -# The resulting output is similar to MultiDocumenter's navigation menu. -# It checks all HTML files in the specified directory and its subdirectories, -# removes any existing navbar, then inserts the new navbar right after . - -# Function to print usage -print_usage() { - echo "Usage: $0 [--exclude ]" - echo " --exclude: Optional comma-separated list of paths to exclude" -} - -# Check if the minimum number of arguments are provided -if [ "$#" -lt 2 ]; then - print_usage - exit 1 -fi - -# Directory containing HTML files -HTML_DIR=$1 -# Source URL of the navigation bar HTML file -NAVBAR_SOURCE_URL=$2 -# Shift off the first two arguments so we can parse the rest -shift 2 - -# Initialize exclude list -EXCLUDE_LIST="" - -# Parse optional arguments -while [[ $# -gt 0 ]]; do - key="$1" - case $key in - --exclude) - EXCLUDE_LIST="$2" - shift 2 - ;; - *) - echo "Unknown option: $1" - print_usage - exit 1 - ;; - esac -done - -# Determine if NAVBAR_SOURCE is a URL (starts with http or https) or a file path -if [[ $NAVBAR_SOURCE_URL == http* ]]; then - NAVBAR_HTML=$(curl -s "$NAVBAR_SOURCE_URL") -else - NAVBAR_HTML=$(cat "$NAVBAR_SOURCE_URL") -fi - -# Check if the download was successful -if [ -z "$NAVBAR_HTML" ]; then - echo "Failed to download navbar HTML from '$NAVBAR_SOURCE_URL'" - exit 1 -fi - -# Function to check if a file should be excluded -should_exclude() { - local file="$1" - IFS=',' read -ra EXCLUDE_PATHS <<< "$EXCLUDE_LIST" - for exclude_path in "${EXCLUDE_PATHS[@]}"; do - if [[ "$file" == *"$exclude_path"* ]]; then - return 0 # Should exclude - fi - done - return 1 # Should not exclude -} - -# Find and process each HTML file -find "$HTML_DIR" -type f -name "*.html" | while read -r file; do - # Check if the file should be excluded - if [ -n "$EXCLUDE_LIST" ] && should_exclude "$file"; then - echo "Skipping excluded file: $file" - continue - fi - - # Remove existing navbar (if any) between and - if grep -q "" "$file"; then - awk '//{flag=1;next}//{flag=0;next}!flag' "$file" > temp && mv temp "$file" - echo "Removed existing navbar from $file" - fi - - # Insert the new navbar right after the first tag using awk - awk -v nav="$NAVBAR_HTML" ' - // { - print $0 - print nav - next - } - { print } - ' "$file" > temp && mv temp "$file" - - # Remove excessive trailing blank lines after insertion - awk 'BEGIN {RS=""; ORS="\n\n"} {gsub(/\n+$/, ""); print}' "$file" > temp_cleaned && mv temp_cleaned "$file" - - echo "Inserted new navbar into $file" -done From 497ecd5240dae31b8823786cf1c668dcee29311d Mon Sep 17 00:00:00 2001 From: Shravan Goswami Date: Thu, 6 Feb 2025 09:07:27 +0530 Subject: [PATCH 2/3] added suggested changes + some other minor improvements --- DocsNav/scripts/insert_navbar.jl | 121 ++++++++++++------------------- 1 file changed, 46 insertions(+), 75 deletions(-) diff --git a/DocsNav/scripts/insert_navbar.jl b/DocsNav/scripts/insert_navbar.jl index 6f8233d..9b19dc4 100644 --- a/DocsNav/scripts/insert_navbar.jl +++ b/DocsNav/scripts/insert_navbar.jl @@ -1,7 +1,7 @@ # insert_navbar.jl # # Usage: -# julia insert_navbar.jl [--exclude "path1,path2,..."] +# julia insert_navbar.jl [--exclude "pat1" "pat2"...] # # Features: # - Processes all .html files in the target (either a single file or recursively in a directory). @@ -13,13 +13,19 @@ # (navbar content) # # - If a file does not contain a tag, that file is skipped. -# - An optional --exclude parameter (comma‑separated) will skip any file whose path contains one of the provided substrings. +# - An optional --exclude parameter (space‑separated) will skip any file whose path contains one of the provided substrings. # -# Required package: HTTP -# -# (Install HTTP via Julia’s package manager, e.g. using Pkg; Pkg.add("HTTP")) +# Installs required packages in a temporary environment. + +using Pkg +Pkg.activate(temp=true) +Pkg.add("HTTP") using HTTP +using Logging + +const START_MARKER = "" +const END_MARKER = "" # --- Utility: Read file contents --- function read_file(filename::String) @@ -36,30 +42,17 @@ function write_file(filename::String, contents::String) end # --- Exclusion Function --- -function should_exclude(filename::String, patterns::Vector{String}) - for pat in patterns - if occursin(pat, filename) - return true - end - end - return false -end +should_exclude(filename::String, patterns::Vector{String}) = any(pat -> occursin(pat, filename), patterns) # --- Remove any existing navbar block and any whitespace following it --- function remove_existing_navbar(html::String) - start_marker = "" - end_marker = "" - while occursin(start_marker, html) && occursin(end_marker, html) - start_idx_range = findfirst(start_marker, html) - end_idx_range = findfirst(end_marker, html) - # Extract the first index from the returned range. + while occursin(START_MARKER, html) && occursin(END_MARKER, html) + start_idx_range = findfirst(START_MARKER, html) + end_idx_range = findfirst(END_MARKER, html) start_idx = first(start_idx_range) - end_idx = first(end_idx_range) - # Get prefix: everything before the start marker (or empty if start_idx is 1) - prefix = start_idx > 1 ? html[1:start_idx-1] : "" - # Suffix: from the end of the end marker to the end of the file, - # then remove any leading whitespace. - suffix = lstrip(html[end_idx + length(end_marker) : end]) + end_idx = first(end_idx_range) + prefix = html[1:start_idx-1] + suffix = lstrip(html[end_idx + length(END_MARKER) : end]) html = string(prefix, suffix) end return html @@ -67,40 +60,32 @@ end # --- Wrap navbar HTML with markers if not already present --- function wrap_navbar(navbar_html::String) - if !occursin("NAVBAR START", navbar_html) || !occursin("NAVBAR END", navbar_html) - return "\n" * navbar_html * "\n" + if !occursin(START_MARKER, navbar_html) || !occursin(END_MARKER, navbar_html) + return string(START_MARKER, "\n", navbar_html, "\n", END_MARKER) else return navbar_html end end # --- Insert new navbar into HTML --- -function insert_navbar(html::String, navbar_html::String) - # Remove any previously inserted navbar block (and any trailing whitespace). +function insert_navbar(html::String, navbar_html::String, filename::String) html = remove_existing_navbar(html) - # Use a regex to find the first tag (case-insensitive). m = match(r"(?i)(]*>)", html) if m === nothing - println("Warning: Could not find tag in the file; skipping insertion.") - return html # Return the unmodified HTML. + @warn "Could not find tag in $(filename); skipping insertion." + return html end - prefix = m.match # The matched tag. - # Build the inserted string: the tag, a newline, the navbar block, and a newline. + prefix = m.match inserted = string(prefix, "\n", navbar_html, "\n") - # Replace only the first occurrence of the tag with our new content. - html = replace(html, prefix => inserted; count = 1) - return html + replace(html, prefix => inserted; count=1) end # --- Process a Single HTML File --- function process_file(filename::String, navbar_html::String) println("Processing: $filename") html = read_file(filename) - html_new = insert_navbar(html, navbar_html) - # If the HTML was not modified because no tag was found, print a message and skip writing. - if html_new == html - println("Skipped: No tag found in $filename") - else + html_new = insert_navbar(html, navbar_html, filename) + if html_new != html write_file(filename, html_new) println("Updated: $filename") end @@ -109,52 +94,38 @@ end # --- Main Function --- function main() if length(ARGS) < 2 - println("Usage: julia insert_navbar.jl [--exclude \"pat1,pat2,...\"]") - return + error("Usage: julia insert_navbar.jl [--exclude \"pat1\" \"pat2\"...]") end + target = ARGS[1] navbar_source = ARGS[2] - - # Process optional --exclude argument. exclude_patterns = String[] - if length(ARGS) ≥ 4 && ARGS[3] == "--exclude" - exclude_patterns = map(x -> string(strip(x)), split(ARGS[4], ',')) + + if length(ARGS) >= 3 + if ARGS[3] == "--exclude" + length(ARGS) < 4 && error("--exclude requires at least one pattern.") + exclude_patterns = ARGS[4:end] + else + error("Invalid argument: $(ARGS[3]). Expected --exclude followed by patterns.") + end end - # --- Get Navbar Content --- - navbar_html = "" - if startswith(lowercase(navbar_source), "http") + navbar_html = if startswith(lowercase(navbar_source), "http") resp = HTTP.get(navbar_source) - if resp.status != 200 - error("Failed to download navbar from $navbar_source") - end - navbar_html = String(resp.body) + resp.status != 200 && error("Failed to download navbar from $navbar_source") + String(resp.body) else - navbar_html = read_file(navbar_source) - end - # Preserve the fetched navbar content exactly; do not modify internal formatting. - navbar_html = string(navbar_html) - # Wrap with markers if not already present. - navbar_html = wrap_navbar(navbar_html) + read_file(navbar_source) + end |> wrap_navbar - # --- Process Files --- if isfile(target) - if !should_exclude(target, exclude_patterns) - process_file(target, navbar_html) - else - println("Skipping excluded file: $target") - end + should_exclude(target, exclude_patterns) ? println("Skipping excluded file: $target") : process_file(target, navbar_html) elseif isdir(target) for (root, _, files) in walkdir(target) for file in files - if endswith(file, ".html") - fullpath = joinpath(root, file) - if !should_exclude(fullpath, exclude_patterns) - process_file(fullpath, navbar_html) - else - println("Skipping excluded file: $fullpath") - end - end + endswith(file, ".html") || continue + fullpath = joinpath(root, file) + should_exclude(fullpath, exclude_patterns) ? println("Skipping excluded file: $fullpath") : process_file(fullpath, navbar_html) end end else From a31d4bcbe930086672532281a89da28ef4761121 Mon Sep 17 00:00:00 2001 From: Shravan Goswami Date: Thu, 6 Feb 2025 09:08:21 +0530 Subject: [PATCH 3/3] remove HTTP from DocsDocumenter --- DocsDocumenter/action.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/DocsDocumenter/action.yml b/DocsDocumenter/action.yml index 1b4268e..9b2588c 100644 --- a/DocsDocumenter/action.yml +++ b/DocsDocumenter/action.yml @@ -42,7 +42,6 @@ runs: shell: julia --color=yes --project=${{ inputs.doc-path }} {0} run: | using Pkg - Pkg.add("HTTP") Pkg.develop(PackageSpec(path=pwd())) Pkg.instantiate()