mirror of
https://github.com/privacyguides/privacyguides.org.git
synced 2025-08-30 14:49:18 +00:00
feat!: Include ZIM files in releases (#3102)
This commit is contained in:
19
tools/archive-releases.sh
Executable file
19
tools/archive-releases.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO="privacyguides/privacyguides.org"
|
||||
BASE_DIR="releases"
|
||||
|
||||
mkdir -p "$BASE_DIR"
|
||||
|
||||
# Get all release tags using gh CLI
|
||||
release_tags=$(gh release list -R "$REPO" --limit 1000 | awk '{print $1}')
|
||||
|
||||
for tag in $release_tags; do
|
||||
target_dir="$BASE_DIR/$tag"
|
||||
mkdir -p "$target_dir"
|
||||
echo "Downloading assets for release: $tag"
|
||||
gh release download "$tag" -R "$REPO" --dir "$target_dir"
|
||||
done
|
||||
|
||||
echo "All releases downloaded."
|
64
tools/delete-unreferenced.sh
Normal file
64
tools/delete-unreferenced.sh
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to find and delete unreferenced asset files
|
||||
# This script searches through all HTML, CSS, and JavaScript files to see if asset files are referenced
|
||||
|
||||
set -e # Exit on any error
|
||||
|
||||
echo -e "Starting unreferenced asset cleanup..."
|
||||
echo "Assets directory: $ASSETS_DIR"
|
||||
echo "Search directory: $SEARCH_DIR"
|
||||
echo ""
|
||||
|
||||
# Check if assets directory exists
|
||||
if [ ! -d "$ASSETS_DIR" ]; then
|
||||
echo -e "Error: Assets directory '$ASSETS_DIR' not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Find all asset files recursively
|
||||
echo "Finding all asset files..."
|
||||
ASSET_FILES=$(find "$ASSETS_DIR" -type f)
|
||||
ASSET_COUNT=$(echo "$ASSET_FILES" | wc -l)
|
||||
echo "Found $ASSET_COUNT asset files"
|
||||
echo ""
|
||||
|
||||
# Find all HTML, CSS, and JavaScript files
|
||||
echo "Finding all HTML, CSS, and JavaScript files..."
|
||||
SEARCH_FILES=$(find "$SEARCH_DIR" \( -name "*.html" -o -name "*.css" -o -name "*.js" \) -type f)
|
||||
SEARCH_COUNT=$(echo "$SEARCH_FILES" | wc -l)
|
||||
echo "Found $SEARCH_COUNT HTML, CSS, and JavaScript files"
|
||||
echo ""
|
||||
|
||||
# Process each asset file
|
||||
echo "Checking each asset file for references..."
|
||||
echo ""
|
||||
|
||||
while IFS= read -r asset_file; do
|
||||
if [ -z "$asset_file" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Get just the filename (without path)
|
||||
asset_filename=$(basename "$asset_file")
|
||||
|
||||
# Search for this filename in all HTML, CSS, and JavaScript files
|
||||
found_reference=false
|
||||
|
||||
while IFS= read -r search_file; do
|
||||
if [ -z "$search_file" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Simple string search for the filename in the file
|
||||
if grep -q "$asset_filename" "$search_file" 2>/dev/null; then
|
||||
found_reference=true
|
||||
break
|
||||
fi
|
||||
done <<< "$SEARCH_FILES"
|
||||
|
||||
if [ "$found_reference" = false ]; then
|
||||
echo -e "Unreferenced: $asset_file"
|
||||
rm "$asset_file"
|
||||
fi
|
||||
done <<< "$ASSET_FILES"
|
102
tools/generate-members.py
Normal file
102
tools/generate-members.py
Normal file
@@ -0,0 +1,102 @@
|
||||
import requests
|
||||
import os
|
||||
|
||||
GITHUB_API_URL = "https://api.github.com/graphql"
|
||||
GITHUB_TOKEN = os.getenv("GH_TOKEN")
|
||||
ORG_NAME = "privacyguides"
|
||||
|
||||
# Fetch members from the API
|
||||
members_api_url = "https://discuss.privacyguides.net/g/members/members.json?offset=0&order=added_at&asc=true"
|
||||
headers = {
|
||||
"Api-Key": os.getenv("DISCOURSE_API_KEY"),
|
||||
"Api-Username": "system"
|
||||
}
|
||||
members_response = requests.get(members_api_url, headers=headers)
|
||||
members_data = members_response.json()
|
||||
|
||||
if 'members' not in members_data:
|
||||
raise KeyError("Response JSON does not contain 'members' key")
|
||||
|
||||
members = members_data['members']
|
||||
public_members_count = 0
|
||||
private_members_count = 0
|
||||
|
||||
html_output = ""
|
||||
for member in members:
|
||||
flair_name = member.get('flair_name')
|
||||
title = member.get('title')
|
||||
if flair_name == "members" or title == "Member":
|
||||
username = member['username']
|
||||
avatar_template = member['avatar_template']
|
||||
avatar_url = f"https://discuss.privacyguides.net{avatar_template.replace('{size}', '128')}"
|
||||
profile_url = f"https://discuss.privacyguides.net/u/{username}"
|
||||
html_output += f'<a href="{profile_url}" target="_blank" title="@{username}" class="mdx-donors__item"><img loading="lazy" src="{avatar_url}"></a>'
|
||||
public_members_count += 1
|
||||
|
||||
# print(html_output)
|
||||
|
||||
query = """
|
||||
{
|
||||
organization(login: "%s") {
|
||||
sponsorshipsAsMaintainer(first: 100) {
|
||||
nodes {
|
||||
sponsorEntity {
|
||||
... on User {
|
||||
login
|
||||
avatarUrl
|
||||
url
|
||||
}
|
||||
... on Organization {
|
||||
login
|
||||
avatarUrl
|
||||
url
|
||||
}
|
||||
}
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""" % ORG_NAME
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {GITHUB_TOKEN}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
response = requests.post(GITHUB_API_URL, json={'query': query}, headers=headers)
|
||||
data = response.json()
|
||||
|
||||
if 'errors' in data:
|
||||
raise Exception(f"GraphQL query failed with errors: {data['errors']}")
|
||||
if 'data' not in data:
|
||||
raise KeyError(f"Response JSON does not contain 'data' key: {data}")
|
||||
|
||||
sponsors = data['data']['organization']['sponsorshipsAsMaintainer']['nodes']
|
||||
|
||||
# Sort sponsors by the date they began their sponsorship
|
||||
sponsors.sort(key=lambda x: x['createdAt'])
|
||||
|
||||
for sponsor in sponsors:
|
||||
sponsor_entity = sponsor['sponsorEntity']
|
||||
login = sponsor_entity['login']
|
||||
avatar_url = sponsor_entity['avatarUrl']
|
||||
url = sponsor_entity['url']
|
||||
html_output += f'<a href="{url}" title="@{login}" rel="ugc nofollow" target="_blank" class="mdx-donors__item"><img loading="lazy" src="{avatar_url}&size=120"></a>'
|
||||
|
||||
# Fetch the number of active members from the Magic Grants API
|
||||
magic_grants_url = "https://donate.magicgrants.org/api/active-members?fund=privacyguides"
|
||||
magic_grants_response = requests.get(magic_grants_url)
|
||||
magic_grants_data = magic_grants_response.json()
|
||||
|
||||
if 'members_count' not in magic_grants_data:
|
||||
raise KeyError("Response JSON does not contain 'members_count' key")
|
||||
|
||||
private_members_count += magic_grants_data['members_count']
|
||||
private_members_count -= public_members_count
|
||||
|
||||
# Append the count of private members
|
||||
if private_members_count > 0:
|
||||
html_output += f'<a href="https://donate.magicgrants.org/privacyguides" class="mdx-donors__item mdx-donors__item--private">+{private_members_count}</a>'
|
||||
|
||||
print(html_output)
|
91
tools/generate-topics.sh
Executable file
91
tools/generate-topics.sh
Executable file
@@ -0,0 +1,91 @@
|
||||
#!/bin/bash
|
||||
|
||||
DATE_CMD="date"
|
||||
|
||||
# Check if the script is running on macOS
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
DATE_CMD="gdate"
|
||||
fi
|
||||
|
||||
# Defaults
|
||||
source="https://discuss.privacyguides.net/top.json?period=weekly"
|
||||
tag="top posts"
|
||||
destination="./site/en/index.html"
|
||||
count=3
|
||||
|
||||
for arg in "$@"
|
||||
do
|
||||
case $arg in
|
||||
--source=*)
|
||||
source="${arg#*=}"
|
||||
shift
|
||||
;;
|
||||
--tag=*)
|
||||
tag="${arg#*=}"
|
||||
shift
|
||||
;;
|
||||
--destination=*)
|
||||
destination="${arg#*=}"
|
||||
shift
|
||||
;;
|
||||
--count=*)
|
||||
count="${arg#*=}"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# URL of the Discourse top.json
|
||||
DISCOURSE_URL="$source"
|
||||
|
||||
# Fetch the JSON data
|
||||
json_data="$(curl -s "$DISCOURSE_URL")"
|
||||
|
||||
# Extract the first 3 topics
|
||||
topics=$(echo "$json_data" | jq -r ".topic_list.topics[:$count]")
|
||||
|
||||
users=$(echo "$json_data" | jq -r ".users")
|
||||
# Generate HTML for the first 3 posts
|
||||
html_output=""
|
||||
for row in $(echo "${topics}" | jq -r '.[] | @base64'); do
|
||||
_jq() {
|
||||
echo "${row}" | base64 --decode | jq -r "${1}"
|
||||
}
|
||||
|
||||
title="$(_jq '.title')"
|
||||
id=$(_jq '.id')
|
||||
like_count=$(_jq '.like_count')
|
||||
reply_count=$(_jq '.posts_count')
|
||||
views=$(_jq '.views')
|
||||
|
||||
author_id=$(_jq '.posters[0].user_id')
|
||||
author_info=$(echo "${users}" | jq -r ".[] | select(.id==$author_id)")
|
||||
author_username=$(echo "${author_info}" | jq -r ".username")
|
||||
|
||||
html_output+="<li class='discourse-topic'>"
|
||||
html_output+="<p class='discourse-title'><a href='https://discuss.privacyguides.net/t/${id}'><strong>${title}</strong></a></p>"
|
||||
html_output+="<hr>"
|
||||
html_output+="<p class='discourse-author'>"
|
||||
html_output+="<span class='discourse-author'>"
|
||||
if [[ -z "$BUILD_OFFLINE" ]]; then
|
||||
html_output+="<img src='https://forum-cdn.privacyguides.net/user_avatar/discuss.privacyguides.net/${author_username}/48/1.png' loading='lazy' aria-hidden='true' alt='${author_username}' width='20' height='20' class='middle'>"
|
||||
fi
|
||||
html_output+="<span> Posted by <em>$author_username</em></span>"
|
||||
html_output+="</span>"
|
||||
html_output+="</p>"
|
||||
html_output+="<p class='discourse-data'>"
|
||||
html_output+="<span class='twemoji'><svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><title>eye</title><path d='M12,9A3,3 0 0,0 9,12A3,3 0 0,0 12,15A3,3 0 0,0 15,12A3,3 0 0,0 12,9M12,17A5,5 0 0,1 7,12A5,5 0 0,1 12,7A5,5 0 0,1 17,12A5,5 0 0,1 12,17M12,4.5C7,4.5 2.73,7.61 1,12C2.73,16.39 7,19.5 12,19.5C17,19.5 21.27,16.39 23,12C21.27,7.61 17,4.5 12,4.5Z' /></svg></span>"
|
||||
html_output+="<span class='discourse-views'> ${views} </span>"
|
||||
html_output+="<span class='twemoji pg-red'><svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><title>heart</title><path d='M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z' /></svg></span>"
|
||||
html_output+="<span class='discourse-likes'> ${like_count} </span>"
|
||||
html_output+="<span class='twemoji'><svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><title>reply</title><path d='M10,9V5L3,12L10,19V14.9C15,14.9 18.5,16.5 21,20C20,15 17,10 10,9Z' /></svg></span>"
|
||||
html_output+="<span class='discourse-replies'> ${reply_count} </span>"
|
||||
html_output+="</p>"
|
||||
html_output+="</li>"
|
||||
done
|
||||
|
||||
tempfile=$(mktemp)
|
||||
echo "$html_output" > "$tempfile"
|
||||
|
||||
# Insert the HTML output between the comments in index.html
|
||||
sed -i'.bak' "/<!-- start $tag -->/,/<!-- end $tag -->/{//!d;}; /<!-- start $tag -->/r $tempfile" "$destination"
|
20
tools/symlink-duplicates.sh
Normal file
20
tools/symlink-duplicates.sh
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
declare -A file_hashes
|
||||
|
||||
find . -type f | while read -r file; do
|
||||
hash=$(sha256sum "$file" | awk '{print $1}')
|
||||
if [[ -n "${file_hashes[$hash]+_}" ]]; then
|
||||
# Duplicate found, replace with symlink to first copy
|
||||
first="${file_hashes[$hash]}"
|
||||
# Remove the duplicate file
|
||||
rm "$file"
|
||||
# Create symlink (relative path)
|
||||
ln -s "$(realpath --relative-to="$(dirname "$file")" "$first")" "$file"
|
||||
echo "Replaced duplicate: $file -> $first"
|
||||
else
|
||||
# First time seeing this hash
|
||||
file_hashes[$hash]="$file"
|
||||
fi
|
||||
done
|
Reference in New Issue
Block a user