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

Add Table of Contents check to CI, and bot script to regenerate #5618

Merged
merged 6 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions .github/workflows/check-toc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: Check Table of Contents (comment /regenerate-toc to auto-fix)

on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
check-formatting:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./docs/build_toc.sh --check-only
82 changes: 82 additions & 0 deletions .github/workflows/regenerate-toc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Regenerate TOC
on:
repository_dispatch:
types: [regenerate-toc-command]
jobs:
regenerate-toc:
runs-on: ubuntu-latest
steps:
- name: Checkout PR branch
uses: actions/checkout@v4
with:
token: ${{ secrets.SLANGBOT_PAT }}
repository: ${{ github.event.client_payload.pull_request.head.repo.full_name }}
ref: ${{ github.event.client_payload.pull_request.head.ref }}
path: pr-branch

- name: Checkout target branch
uses: actions/checkout@v4
with:
token: ${{ secrets.SLANGBOT_PAT }}
repository: ${{ github.event.client_payload.pull_request.base.repo.full_name }}
ref: ${{ github.event.client_payload.pull_request.base.ref }}
path: target-branch

- name: Regenerate Table of Contents
id: regen
run: |
./target-branch/docs/build_toc.sh --source ./pr-branch

- name: Configure Git commit signing
id: git-info
run: |
echo "${{ secrets.SLANGBOT_SIGNING_KEY }}" > "${{runner.temp}}"/signing_key
chmod 600 "${{runner.temp}}"/signing_key
git -C pr-branch config commit.gpgsign true
git -C pr-branch config gpg.format ssh
git -C pr-branch config user.signingkey "${{runner.temp}}"/signing_key
bot_info=$(curl -s -H "Authorization: Bearer ${{ secrets.SLANGBOT_PAT }}" \
"https://api.github.com/user")
echo "bot_identity=$(echo $bot_info | jq --raw-output '.login + " <" + (.id|tostring) + "+" + .login + "@users.noreply.github.com>"')" >> $GITHUB_OUTPUT
echo "bot_name=$(echo $bot_info | jq --raw-output '.login')" >> $GITHUB_OUTPUT

- name: Create Pull Request
id: create-pr
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.SLANGBOT_PAT }}
path: pr-branch
commit-message: "regenerate documentation Table of Contents"
title: "Regenerate documentation ToC for PR #${{ github.event.client_payload.pull_request.number }}"
body: "Automated ToC generation for ${{ github.event.client_payload.pull_request.html_url }}"
committer: ${{ steps.git-info.outputs.bot_identity }}
author: ${{ steps.git-info.outputs.bot_identity }}
branch: regenerate-toc-${{ github.event.client_payload.pull_request.number }}-${{ github.event.client_payload.pull_request.head.ref }}
base: ${{ github.event.client_payload.pull_request.head.ref }}
push-to-fork: ${{ steps.git-info.outputs.bot_name }}/slang
delete-branch: true

- name: Comment on PR
uses: peter-evans/create-or-update-comment@v4
if: always()
with:
token: ${{ secrets.SLANGBOT_PAT }}
repository: ${{ github.event.client_payload.github.payload.repository.full_name }}
issue-number: ${{ github.event.client_payload.pull_request.number }}
body: |
${{
steps.regen.conclusion == 'failure'
&& format('❌ Table of Contents generation failed. Please check the [workflow run](https://github.com/{0}/actions/runs/{1})', github.repository, github.run_id)
|| (steps.create-pr.conclusion == 'failure'
&& format('❌ Failed to create regenerate ToC pull request. Please check the [workflow run](https://github.com/{0}/actions/runs/{1})', github.repository, github.run_id)
|| format('🌈 Regenerated Table of Contents, please merge the changes from [this PR]({0})', steps.create-pr.outputs.pull-request-url))
}}

- name: Add reaction
uses: peter-evans/create-or-update-comment@v4
with:
token: ${{ secrets.SLANGBOT_PAT }}
repository: ${{ github.event.client_payload.github.payload.repository.full_name }}
comment-id: ${{ github.event.client_payload.github.payload.comment.id }}
reactions-edit-mode: replace
reactions: hooray
5 changes: 5 additions & 0 deletions .github/workflows/slash-command-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ jobs:
"command": "format",
"permission": "none",
"issue_type": "pull-request"
},
{
"command": "regenerate-toc",
"permission": "none",
"issue_type": "pull-request"
}
]

Expand Down
127 changes: 127 additions & 0 deletions docs/build_toc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/usr/bin/env bash
set -e

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
project_root="$(dirname "$script_dir")"
check_only=0

show_help() {
me=$(basename "$0")
cat <<EOF
$me: Build table of contents for documentation directories

Usage: $me [--help] [--source <path>] [--check-only]

Options:
--help Show this help message
--source Path to project root directory (defaults to parent of the script directory)
--check-only Check if TOC needs updating, exit 1 if changes needed
EOF
}

while [[ "$#" -gt 0 ]]; do
case $1 in
-h | --help)
show_help
exit 0
;;
--source)
project_root="$2"
shift
;;
--check-only)
check_only=1
;;
*)
echo "unrecognized argument: $1" >&2
show_help >&2
exit 1
;;
esac
shift
done

missing_bin=0

require_bin() {
local name="$1"
if ! command -v "$name" &>/dev/null; then
echo "This script needs $name, but it isn't in \$PATH" >&2
missing_bin=1
return
fi
}

require_bin "mcs"
require_bin "mono"

if [ "$missing_bin" -eq 1 ]; then
exit 1
fi

temp_dir=$(mktemp -d)
trap 'rm -rf "$temp_dir"' EXIT

cd "$project_root/docs" || exit 1

cat >"$temp_dir/temp_program.cs" <<EOL
$(cat "$script_dir/scripts/Program.cs")

namespace toc
{
class Program
{
static int Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Please provide a directory path");
return 1;
}

try
{
Builder.Run(args[0]);
return 0;
}
catch (Exception ex)
{
Console.WriteLine(\$"Error: {ex.Message}");
return 1;
}
}
}
}
EOL

if ! mcs -r:System.Core "$temp_dir/temp_program.cs" -out:"$temp_dir/toc-builder.exe"; then
echo "Compilation of $script_dir/scripts/Program.cs failed" >&2
exit 1
fi

for dir in "user-guide" "gfx-user-guide"; do
if [ -d "$script_dir/$dir" ]; then
if [ "$check_only" -eq 1 ]; then
# Ensure working directory is clean
if ! git diff --quiet "$script_dir/$dir/toc.html" 2>/dev/null; then
echo "Working directory not clean, cannot check TOC" >&2
exit 1
fi
fi

if ! mono "$temp_dir/toc-builder.exe" "$script_dir/$dir"; then
echo "TOC generation failed for $dir" >&2
exit 1
fi

if [ "$check_only" -eq 1 ]; then
if ! git diff --quiet "$script_dir/$dir/toc.html" 2>/dev/null; then
git diff --color "$script_dir/$dir/toc.html"
git checkout -- "$script_dir/$dir/toc.html" 2>/dev/null
exit 1
fi
fi
else
echo "Directory $dir not found" >&2
fi
done
7 changes: 4 additions & 3 deletions docs/scripts/Program.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Collections.Generic;
namespace toc
{
public class Builder
Expand Down Expand Up @@ -128,7 +129,7 @@ public static string Run(string path)
{
StringBuilder outputSB = new StringBuilder();
outputSB.AppendFormat("Building table of contents from {0}...\n", path);
var files = System.IO.Directory.EnumerateFiles(path, "*.md");
var files = System.IO.Directory.EnumerateFiles(path, "*.md").OrderBy(f => System.IO.Path.GetFileName(f));
List<Node> nodes = new List<Node>();
foreach (var f in files)
{
Expand Down Expand Up @@ -230,4 +231,4 @@ public static string Run(string path)
return outputSB.ToString();
}
}
}
}
Loading