Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bdocs: improvements + script for updating old links #8652

Merged
merged 30 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f2222e4
bdocs: improvements + script for updating old links
internetisaiah Dec 18, 2024
0d3fe61
script v1
internetisaiah Dec 19, 2024
e5de33d
script v2
internetisaiah Dec 19, 2024
2c7f2af
script v3
internetisaiah Dec 19, 2024
1a06e59
script v4
internetisaiah Dec 19, 2024
dd0bc08
script v4
internetisaiah Dec 19, 2024
677685e
script v5
internetisaiah Dec 19, 2024
60b6ea5
Merge branch 'bdocs-update-links' of github.com:braze-inc/braze-docs …
internetisaiah Dec 19, 2024
9f31805
fixing test.py git issue
internetisaiah Dec 19, 2024
e72d712
resorting branch files
internetisaiah Dec 19, 2024
9af79e0
Merge branch 'develop' into bdocs-update-links
internetisaiah Dec 19, 2024
8373254
reverting redirect list to develop
internetisaiah Dec 19, 2024
21ad492
script v6
internetisaiah Dec 19, 2024
bb99f60
update script v1
internetisaiah Dec 19, 2024
d94cb65
pseudo_code notes
internetisaiah Dec 19, 2024
aa46789
changed filename
internetisaiah Dec 19, 2024
ae6489c
cleanup
internetisaiah Dec 20, 2024
ee714cc
setting up bdocs
internetisaiah Dec 20, 2024
8bcdaa3
Delete t.md
internetisaiah Dec 20, 2024
01f1b22
Delete pseudo_code.txt
internetisaiah Dec 20, 2024
769e840
cleanup
internetisaiah Dec 20, 2024
c11cc76
moving mrd to scripts/utils
internetisaiah Dec 20, 2024
a552bb0
Update bdocs
internetisaiah Dec 21, 2024
800edc6
quick fix for temp dir
internetisaiah Dec 21, 2024
fd779f0
making the good link bad again lol
internetisaiah Dec 23, 2024
b2ac112
better handling of trailing '/' chars
internetisaiah Dec 23, 2024
4d7bfe1
fixing test page
internetisaiah Dec 23, 2024
8086f9c
full fix for test page
internetisaiah Dec 23, 2024
a5e94c9
replacing teset bad link
internetisaiah Dec 23, 2024
5221508
adding docs for new script
internetisaiah Dec 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ jekyll-algolia-*
# Jetbrains IDEs
.idea/
braze-docs.iml

# Braze scripts
scripts/temp/*
114 changes: 69 additions & 45 deletions bdocs
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
#!/bin/bash

# This is a bash script for interacting with the various files in './scripts/'.
# This is a wrapper script for interacting with the files in './scripts/'.
#
# Usage: ./bdocs [option]

set -e

# The project's root directory.
export PROJECT_ROOT
PROJECT_ROOT="$(dirname "$(realpath "$0")")"
# The project's root directory and redirect file.
export PROJECT_ROOT="$(dirname "$(realpath "$0")")"
export REDIRECT_FILE="./assets/js/broken_redirect_list.js"
export REDIRECT_MATCHES="./scripts/temp/redirect_matches.json"

# All scripts exported so they can source bdocs and call each other if needed.
export DEPLOY="$PROJECT_ROOT/scripts/create_deploy_text.sh"
export RELEASE="$PROJECT_ROOT/scripts/create_release_text.sh"
export TLINKS="$PROJECT_ROOT/scripts/transform_reference_links.py"
export RLINKS="$PROJECT_ROOT/scripts/remove_unused_reference_links.rb"
export ULINKS="$PROJECT_ROOT/scripts/update_old_links.py"
export LREDIRECTS="$PROJECT_ROOT/scripts/list_new_redirect_urls.sh"
# Utility scripts that are not directly used in bdocs:
export MRD="$PROJECT_ROOT/scripts/utils/merge_redirect_descendants.py"
export TEMP_DIR="$PROJECT_ROOT/scripts/temp"

# Displays usage for bdocs
display_help() {
Expand All @@ -19,72 +31,84 @@ USAGE:
./bdocs [option]

OPTIONS:
deploy Create the deploy body text for weekly deployments
release Create the release body text for monthly releases
tlinks Transform reference links to inline links on 1 or more pages
rlinks Remove reference links that are not being used on 1 or more pages
redirects List the old URLs for all new redirects in this branch
help Display this help message and exit
deploy Create the deploy body text for weekly deployments
release Create the release body text for monthly releases
tlinks Transform reference links to inline links on 1 or more pages
rlinks Remove unused reference links on 1 or more pages
ulinks Update old links using newest redirect on 1 or more pages
lredirects Test new redirects by listing old URLs in this branch
help Display this help message and exit

EOF
}

# If no './scripts/temp' directory, create one.
if [ ! -d "$TEMP_DIR" ]; then
mkdir "$TEMP_DIR"
fi

# If a file or directory is required, pass or fail.
require_path_or_file() {
if [[ -z "$1" ]]; then
echo "Error: A file or directory path is required."
exit 1
fi
}

# If new merges into 'develop' are required, pass or fail.
require_new_merges() {
LATEST_COMMIT_HASH=$(git log --max-count=1 --format="%H" origin/master ^origin/develop)
COMMIT_LOGS=$(git log --first-parent "$LATEST_COMMIT_HASH"..origin/develop)
if [ -z "$COMMIT_LOGS" ]; then
echo "Error: No new merges into 'develop' since the last deployment."
exit 1
fi
}

# Check if no arguments were provided
if [[ $# -eq 0 ]]; then
display_help
exit 1
fi

# Fetch the latest changes from the remote quietly.
git fetch origin develop --quiet

# Argument parsing
case $1 in
deploy)
require_new_merges
if [[ $# -eq 3 ]]; then
"$PROJECT_ROOT/scripts/create_deploy_text.sh" "$2" "$3"
"$DEPLOY" "$2" "$3"
else
"$PROJECT_ROOT/scripts/create_deploy_text.sh"
"$DEPLOY"
fi
;;
release)
"$PROJECT_ROOT/scripts/create_release_text.sh"
require_new_merges
"$RELEASE"
;;
tlinks)
if [[ -z "$2" ]]; then
echo "Error: A file or directory path is required."
exit 1
fi
python3 "$PROJECT_ROOT/scripts/transform_reference_links.py" "$2"
echo "Success!"
while true; do
echo "Do you want to remove the unused reference links? [Y/n]."
read -r opt
case $opt in
y*|Y*)
ruby "$PROJECT_ROOT/scripts/remove_unused_reference_links.rb" "$2"
echo "Success!"
break
;;
n*|N*)
echo "The unused reference links were left untouched."
break
;;
*) echo "Error: Invalid choice."
echo ""
;;
esac
done
require_path_or_file "$2"
"$TLINKS" "$2"
"$RLINKS" "$2" # Run rlinks next, to clean up unused reference links.
;;
rlinks)
if [[ -z "$2" ]]; then
echo "Error: The path to file or directory is required."
exit 1
fi
ruby "$PROJECT_ROOT/scripts/remove_unused_reference_links.rb" "$2"
require_path_or_file "$2"
"$RLINKS" "$2"
;;
ulinks)
require_path_or_file "$2"
touch "$REDIRECT_MATCHES"
"$MRD"
"$ULINKS" "$2"
# rm "$REDIRECT_MATCHES"
;;
redirects)
lredirects)
if [[ $# -eq 2 ]]; then
"$PROJECT_ROOT/scripts/list_redirect_urls.sh" "$2"
"$LREDIRECTS" "$2"
else
"$PROJECT_ROOT/scripts/list_redirect_urls.sh"
"$LREDIRECTS"
fi
;;
help)
Expand Down
4 changes: 2 additions & 2 deletions scripts/create_release_text.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ main() {
COMMIT_TITLE=$(echo "$commit" | jq -r '.title')
COMMIT_BODY=$(echo "$commit" | jq -r '.body')

# Print the deploy text for each deployment.
# Print the deploy text for each deployment using the sourced DEPLOY.
echo "## $COMMIT_BODY"
./scripts/create_deploy_text.sh "$PREV_COMMIT_DATE" "$COMMIT_DATE"
"$DEPLOY" "$PREV_COMMIT_DATE" "$COMMIT_DATE"
echo ""

# Get the next range of commits by increasing 'PREV_COMMIT_DATE'.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@
# base URL to list all old URLs so the user can open old links directly from
# the terminal to test redirects.
#
# Usage: ./bdocs redirects

# Fetch the latest changes from the remote.
git fetch origin develop --quiet
# Usage: ./bdocs lredirects

# Check new redirects by comparing the current branch to develop.
NEW_REDIRECTS=$(git diff develop -- $PROJECT_ROOT/assets/js/broken_redirect_list.js)
NEW_REDIRECTS=$(git diff develop -- $REDIRECT_FILE)

# If there's no differences, print an error message and exit.
if [[ -z "$NEW_REDIRECTS" ]]; then
Expand All @@ -22,7 +19,6 @@ fi
# Check if a base URL was passed as an argument from bdocs, otherwise prompt the user.
if [[ -z "$1" ]]; then
echo "Which base URL would you like to use? Note: You can use a local or deployment base URL."
echo ""
read BASE_URL
else
BASE_URL=$1
Expand Down
2 changes: 2 additions & 0 deletions scripts/remove_unused_reference_links.rb
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#!/usr/bin/env ruby

# Removes unused reference-style links from the bottom of a file, such as:
# [1]: {{site.baseurl}}/contributing/your_first_contribution/
#
Expand Down
14 changes: 14 additions & 0 deletions scripts/tests/update_old_links.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
This is a test page for testing `ulinks`. When adding "bad links", the syntax needs to match the syntax on this page:

https://www.braze.com/docs/contributing/content_management/cross_referencing

i.e. no `/docs` should be found in the `site.baseurl`.

Here's some bad links:
1. [Bad link 1]({{site.baseurl}}/best_practices/).
2. [Bad link 2]({{site.baseurl}}/best_practices/#android-push-category).
3. [Bad link 3]({{site.baseurl}}/user_guide/message_building_by_channel/email/link_templates/).

Here's two good links:
1. [Good link 1](https://www.braze.com/docs/developer_guide/platform_wide/getting_started/analytics_overview)
2. [Good link 2]({{site.baseurl}}/developer_guide/getting_started/analytics_overview)
9 changes: 8 additions & 1 deletion scripts/transform_reference_links.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Converts Markdown reference links to in-line links. Created because
#!/usr/bin/env python3

# Transforms Markdown reference links to in-line links. Created because
# reference links cannot be placed inside Liquid {% tab %} tags.
#
# For more information, see:
Expand All @@ -14,6 +16,7 @@
import sys
import re


# Create a dictionary with all reference links at the bottom of the file.
def create_link_dictionary(file_path):
link_dict = {}
Expand All @@ -40,6 +43,7 @@ def create_link_dictionary(file_path):

return link_dict


# Use the dictionary to find and replace all references with the full link.
def replace_links(file_path, link_dict):
with open(file_path, 'r') as file:
Expand All @@ -57,6 +61,8 @@ def replace_links(file_path, link_dict):
with open(file_path, 'w') as file:
file.writelines(updated_lines)


# TODO: Move this to bdocs directly for easier reuse.
# Recursively convert links for all Markdown files in given directory.
def process_directory(directory):
for root, dirs, files in os.walk(directory):
Expand All @@ -66,6 +72,7 @@ def process_directory(directory):
link_dict = create_link_dictionary(file_path)
replace_links(file_path, link_dict)


# If arg == directory, convert links for all Markdown files in that directory.
# If arg == file, convert links for that Markdown file only.
def main(path):
Expand Down
125 changes: 125 additions & 0 deletions scripts/update_old_links.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/env python3

# Uses 'assets/js/broken_redirect_list.js' to determine the newest version of
# a link, then updates all 'OLD' links with the 'NEW' link for the given
# page or directory.
#
# Requires: 'update_redirect_list.py'
#
# Usage: ./bdocs ulinks [FILE|DIRECTORY]
#
# Options:
# FILE Updates old links in a single file.
# DIRECTORY Recursively updates old links in a directory.

import os
import json
import re
import sys

# Get project root
PROJECT_ROOT = os.environ.get('PROJECT_ROOT')
REDIRECT_MATCHES = os.environ.get('REDIRECT_MATCHES')


def update_old_links(filepath, redirects):
# redirects: { key: { "new_url": str, "old_urls": [str, ...] }, ... }
# Replace ({{site.baseurl}}old_url) with ({{site.baseurl}}new_url)
if not os.path.isfile(filepath):
return 0

with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
content = f.read()

original_content = content
total_replacements = 0

for entry_key, data in redirects.items():
new_url = data["new_url"]
old_urls = data["old_urls"]

for old in old_urls:
# If there's an anchor (#), match exactly.
# Otherwise, match with or without trailing slash.
if "#" in old:
# Exact match
pattern = r"\(" + re.escape("{{site.baseurl}}") + re.escape(old) + r"\)"
else:
# old.rstrip('/') removes any trailing slash
# The /? makes the slash optional in the pattern
old_noslash = old.rstrip('/')
pattern = r"\(" + re.escape("{{site.baseurl}}") + re.escape(old_noslash) + r"/?\)"

# Count how many times the old link appears
count_before = len(re.findall(pattern, content))
if count_before > 0:
# Replace all occurrences with the new URL
content = re.sub(pattern,
"(" + "{{site.baseurl}}" + new_url + ")",
content)
total_replacements += count_before

# If content changed, write the file back out
if content != original_content:
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)

return total_replacements


def get_redirect_matches(json_file):
if not os.path.exists(json_file):
print(f"Error: '{json_file}' not found.")
exit(1)
with open(json_file, 'r') as f:
data_dict = json.load(f)
return data_dict


# TODO: Move this to bdocs directly for easier reuse.
def process_directory(directory, redirects):
total_global_replacements = 0
for root, dirs, files in os.walk(directory):
for fn in files:
file_path = os.path.join(root, fn)
replacements = update_old_links(file_path, redirects)
if replacements > 0:
# Print relative path from the given directory
relative_path = os.path.relpath(file_path, start=directory)
print(f"In '{relative_path}', made {replacements} replacements.")
total_global_replacements += replacements
return total_global_replacements


# TODO: Move this to bdocs directly for easier reuse.
def process_single_file(filepath, redirects):
replacements = update_old_links(filepath, redirects)
if replacements > 0:
# When given a single file, just print the filename
print(f"In '{os.path.basename(filepath)}', made {replacements} replacements.")
return replacements


def main(path):
redirects = get_redirect_matches(REDIRECT_MATCHES)

if os.path.isdir(path):
# Process directory
total_replacements = process_directory(path, redirects)
print(f"Total replacements made across all files: {total_replacements}")
elif os.path.isfile(path):
# Process single file
total_replacements = process_single_file(path, redirects)
print(f"Total replacements made: {total_replacements}")
else:
print(f"Invalid path: {path}. Please provide a valid directory or file.")
sys.exit(1)


if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python script.py <directory_or_file>")
sys.exit(1)
user_path = sys.argv[1]
user_path = os.path.abspath(user_path)
main(user_path)
Loading