Skip to content

🧘 [just] gh-process v5.8 utilizes gh-observer, adds copilot features, and fixes bugs#32

Merged
chicks-net merged 2 commits intomainfrom
chicks/2026-03-31-gh-process-5-8
Mar 31, 2026
Merged

🧘 [just] gh-process v5.8 utilizes gh-observer, adds copilot features, and fixes bugs#32
chicks-net merged 2 commits intomainfrom
chicks/2026-03-31-gh-process-5-8

Conversation

@chicks-net
Copy link
Copy Markdown
Member

Done

  • 🧘 [just] gh-process v5.8 utilizes gh-observer, adds copilot features, and fixes bugs
  • disable shellcheck in pr-hook

Meta

(Automated in .just/gh-process.just.)

Copilot AI review requested due to automatic review settings March 31, 2026 22:59
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the repository’s just-based automation to align with gh-process v5.8 by adding template-sync tooling, repo metadata generation, Copilot/Claude helpers, and a few workflow fixes (notably PR body “Done” section markers and gh-observer support).

Changes:

  • Add template synchronization system (checksum manifest, update/verify/diff recipes, and update logic scripts).
  • Add .repo.toml → shell env generation plus expanded CUE schema/verification (including standard-release flag).
  • Enhance PR workflow automation: PR body markers + robust Done-section updater, checks watching via gh observer, and Copilot/Claude review helpers.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
justfile Imports additional .just/* modules (template sync, repo-toml, hooks, Copilot/Claude).
docs/repo-toml.cue Adds flags.standard-release to the .repo.toml schema.
.just/template-sync.just Adds recipes for checksum generation, verification, template updates, and diffing.
.just/shellcheck.just Moves recipe group to Testing/Compliance.
.just/repo-toml.sh Adds generated repo metadata/flags shell exports.
.just/repo-toml.just Adds generator/checker recipes for .just/repo-toml.sh from .repo.toml.
.just/pr-hook.just Adds a pre-PR hook recipe (shellcheck disabled, sorts Claude permissions).
.just/lib/update_pr_body.sh Adds robust PR body Done-section updater using HTML markers + fallback parsing.
.just/lib/template_update.sh Adds core manifest-driven template update logic.
.just/lib/template_sync_test.sh Adds fixture-driven tests for template update logic (with mocked curl).
.just/lib/pr_body_test.sh Adds test runner for PR body update logic.
.just/lib/install-prerequisites.sh Adds helper script to install/check required CLI tooling.
.just/lib/generate_checksums.sh Adds script to generate versioned checksums manifest from git history.
.just/lib/common.sh Adds shared checksum helper for template sync scripts.
.just/gh-process.just Updates PR creation/body markers, adds check polling, gh observer usage, conditional release workflow, and enhanced Copilot/Claude review output.
.just/cue-verify.just Moves group + adds GitHub metadata sync checks and cue-sync-from-github recipe.
.just/copilot.just Adds Copilot suggestion picker, refresh, and rollback tooling.
.just/claude.just Adds Claude settings permission sorting/check helpers.
.just/CHECKSUMS.json Adds checksum manifest used for template synchronization.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 217 to +223
# watch GHAs then check for Copilot suggestions
[group('Process')]
pr_checks: _on_a_pull_request && claude_review
pr_checks: && claude_review
#!/usr/bin/env bash

gh pr checks --watch -i 5
# Use gh observer if installed, otherwise fall back to gh pr checks --watch
if gh observer --help >/dev/null 2>&1; then
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pr_checks no longer runs the _on_a_pull_request sanity check (it was removed from the recipe header). Since the body calls gh pr checks / gh observer and reads PR metadata, this can now fail or behave confusingly when run from a branch without an open PR. Add _on_a_pull_request back as a pre-dependency (or call it at the start of the script body) so the recipe fails fast with the existing clear message/exit code.

Copilot uses AI. Check for mistakes.
Comment on lines +37 to +38
ORG_NAME=$(echo "$WEB_URL" | sed -E 's|https://github.com/([^/]+)/.*|\1|')
REPO_NAME=$(echo "$WEB_URL" | sed -E 's|https://github.com/[^/]+/(.+)|\1|')
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REPO_NAME parsing from web_url doesn’t handle a trailing slash: for https://github.com/org/repo/ it will capture repo/. Consider using the same extraction used in .just/cue-verify.just (strip an optional trailing slash) or normalize WEB_URL first, so REPO_REPO_NAME is stable.

Suggested change
ORG_NAME=$(echo "$WEB_URL" | sed -E 's|https://github.com/([^/]+)/.*|\1|')
REPO_NAME=$(echo "$WEB_URL" | sed -E 's|https://github.com/[^/]+/(.+)|\1|')
ORG_NAME=$(echo "$WEB_URL" | sed -E 's|https://github.com/([^/]+)/[^/]+/?$|\1|')
REPO_NAME=$(echo "$WEB_URL" | sed -E 's|https://github.com/[^/]+/([^/]+)/?$|\1|')

Copilot uses AI. Check for mistakes.
Comment on lines +100 to +113
# Extract line number from selection to match back to original comment
line_num=$(echo "$selected" | cut -d':' -f2 | cut -d' ' -f1)

# Validate line number is numeric
if ! [[ "$line_num" =~ ^[0-9]+$ ]]; then
echo "{{RED}}Error: Could not extract line number from selection{{NORMAL}}"
exit 1
fi

# Get the full suggestion data
suggestion_data=$(jq -r --arg line "$line_num" '.[] | select(.originalLine == ($line | tonumber))' "$copilot_comments")
file_path=$(echo "$suggestion_data" | jq -r '.path')
original_line=$(echo "$suggestion_data" | jq -r '.originalLine')
suggestion_body=$(echo "$suggestion_data" | jq -r '.body')
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The selected suggestion is re-identified using only originalLine. If multiple Copilot comments share the same line number (especially across different files), suggestion_data will contain multiple JSON objects and file_path/suggestion_body become multi-line/ambiguous, which can lead to applying the wrong suggestion. Match by both path and originalLine (parse both from the gum choose entry) or select by array index.

Copilot uses AI. Check for mistakes.
# Single line: use sed with proper escaping
# shellcheck disable=SC2016
escaped_code=$(echo "$suggested_code" | sed 's|[][\\\.*^$()+?{|/]|\\&|g')
sed -i "${original_line}s|.*|${escaped_code}|" "$file_path"
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sed -i is not portable between GNU sed and BSD sed (macOS requires -i ''). As written, applying a single-line suggestion will fail on macOS. Use a portable in-place edit approach (e.g., write to a temp file and mv, or use perl -pi -e) or branch on OS/sed flavor.

Suggested change
sed -i "${original_line}s|.*|${escaped_code}|" "$file_path"
sed "${original_line}s|.*|${escaped_code}|" "$file_path" > "${file_path}.tmp"
mv "${file_path}.tmp" "$file_path"

Copilot uses AI. Check for mistakes.
Comment on lines +426 to +434

for backup in "$BACKUP_DIR"/*.bak; do
if [[ -f "$backup" ]]; then
filename=$(basename "$backup")
timestamp=${filename##*_}
timestamp=${timestamp%.bak}
# Convert safe path back to original file path
safe_path=${filename%_*}
target_file=${safe_path//_/\/}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backup filenames encode the target path by replacing / with _, but rollback reconstructs the path by replacing every _ with /. This is not reversible when the original path contains underscores, and can restore to the wrong location. Use an unambiguous encoding (e.g., base64/urlencode) or store the original path alongside the backup (manifest file, or include a delimiter/escape scheme).

Suggested change
for backup in "$BACKUP_DIR"/*.bak; do
if [[ -f "$backup" ]]; then
filename=$(basename "$backup")
timestamp=${filename##*_}
timestamp=${timestamp%.bak}
# Convert safe path back to original file path
safe_path=${filename%_*}
target_file=${safe_path//_/\/}
# Decode the original backup target path from the encoded portion of the filename.
# Filenames are expected to be of the form: <base64url-encoded-path>_<timestamp>.bak
# where the path component has been encoded with URL-safe base64.
decode_backup_target() {
local encoded="$1"
python3 - << 'PY'
import sys
import base64
def decode_base64url(s: str) -> str:
# Add required padding for base64 decoding
pad = (-len(s)) % 4
s_padded = s + ("=" * pad)
return base64.urlsafe_b64decode(s_padded.encode("utf-8")).decode("utf-8")
if __name__ == "__main__":
try:
s = sys.argv[1]
sys.stdout.write(decode_base64url(s))
except Exception:
# On any error, write nothing and exit with non-zero for the caller to handle.
sys.exit(1)
PY
}
for backup in "$BACKUP_DIR"/*.bak; do
if [[ -f "$backup" ]]; then
filename=$(basename "$backup")
# Strip the .bak suffix first
base_name=${filename%.bak}
# Extract timestamp (text after the last underscore)
timestamp=${base_name##*_}
# Extract the encoded path (everything before the last underscore)
encoded_path=${base_name%_*}
# Decode the original target file path from the encoded portion
if ! target_file=$(decode_backup_target "$encoded_path"); then
echo "{{RED}}Warning: Skipping backup with invalid encoded path: $filename{{NORMAL}}"
continue
fi

Copilot uses AI. Check for mistakes.
Comment on lines +89 to +96
while IFS= read -r filepath; do
if [[ ! -f "$filepath" ]]; then
# Check if this is a cleaned file
if jq -e --arg fp "$filepath" '.cleaned_files // [] | index($fp) != null' "$MANIFEST_FILE" >/dev/null 2>&1; then
echo " {{BLUE}}⊘{{NORMAL}} $filepath - removed by clean_template"
else
echo " {{YELLOW}}⊘{{NORMAL}} $filepath - not present locally"
all_match=false
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleaned-file detection only checks for exact membership in .cleaned_files, but the manifest includes many paths under .just/test/... while .cleaned_files contains only the directory .just/test. This causes derived repos (like this one, which has no .just/test/) to report many files as missing and fail verification. Treat cleaned entries as path prefixes (skip any file where its path is under a cleaned directory).

Copilot uses AI. Check for mistakes.
Comment on lines +124 to +156
# Check if this is a cleaned file (should be skipped if missing)
local is_cleaned=false
if jq -e --arg fp "$filepath" '.cleaned_files // [] | index($fp) != null' "$MANIFEST_FILE" >/dev/null 2>&1; then
is_cleaned=true
fi

# Get versions array from manifest
local versions_json
versions_json=$(jq -r ".files[\"$filepath\"].versions // []" "$MANIFEST_FILE")

if [[ "$versions_json" == "[]" ]]; then
echo -e " ${YELLOW}⚠${NORMAL} $filepath - not in manifest, skipping"
return
fi

# Get latest version info
local latest_checksum latest_version
latest_checksum=$(echo "$versions_json" | jq -r '.[0].checksum')
latest_version=$(echo "$versions_json" | jq -r '.[0].version')

# Check if file exists locally
if [[ ! -f "$filepath" ]]; then
# If it's a cleaned file, skip it (intentionally removed)
if [[ "$is_cleaned" == true ]]; then
echo -e " ${GREEN}⊘${NORMAL} $filepath - removed by clean_template, skipping"
((skipped_cleaned_count++)) || true
return
fi
echo -e " ${BLUE}↓${NORMAL} $filepath - new file, downloading"
if download_file "$filepath"; then
echo -e " ${GREEN}Downloaded successfully${NORMAL}"
((downloaded_new_count++)) || true
else
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

template_update.sh treats cleaned_files as exact file paths, but the manifest includes many files under .just/test/... while .cleaned_files only lists the directory .just/test. This means missing fixture files will be treated as “new files” and download will be attempted (and likely fail due to missing parent dirs) and/or reintroduce cleaned content. Update the cleaned check to treat entries as directory prefixes and skip any file under a cleaned directory (and consider mkdir -p $(dirname "$filepath") before downloads for new files).

Copilot uses AI. Check for mistakes.
if command -v sha256sum &>/dev/null; then
sha256sum "$file" | awk '{print $1}'
elif command -v shasum &>/dev/null; then
shasum -a 256 "$file" | awk '{print $1}'
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compute_checksum returns an empty string when neither sha256sum nor shasum exists, which can lead to misleading results (comparisons against empty checksums). This should error out with a clear message (similar to .just/lib/common.sh) so verification doesn’t silently misbehave.

Suggested change
shasum -a 256 "$file" | awk '{print $1}'
shasum -a 256 "$file" | awk '{print $1}'
else
echo "{{RED}}Error: No SHA-256 checksum tool found (sha256sum or shasum){{NORMAL}}" >&2
echo "Please install coreutils (sha256sum) or a compatible shasum implementation and retry." >&2
exit 1

Copilot uses AI. Check for mistakes.
Comment on lines +221 to +225
printf '%s\n' \
"# .repo.toml" \
"" \
"[about]" \
"description = \"$GH_DESC\"" \
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When creating a new .repo.toml, the description line interpolates $GH_DESC without escaping quotes/newlines. If the GitHub description contains " (or other characters requiring escaping), the generated TOML will be invalid and the script will delete .repo.toml after failing validation. Escape the description (as you already do for the update-in-place path) or use a TOML-safe quoting strategy.

Suggested change
printf '%s\n' \
"# .repo.toml" \
"" \
"[about]" \
"description = \"$GH_DESC\"" \
# Escape description for safe embedding in TOML basic string
GH_DESC_ESCAPED="${GH_DESC//\"/\\\"}"
GH_DESC_ESCAPED="${GH_DESC_ESCAPED//$'\n'/\\n}"
printf '%s\n' \
"# .repo.toml" \
"" \
"[about]" \
"description = \"$GH_DESC_ESCAPED\"" \

Copilot uses AI. Check for mistakes.
Comment on lines +32 to +35
# Verify test directory exists
if [[ ! -d "$TEST_DIR" ]]; then
echo -e "${RED}Error: Test directory not found: $TEST_DIR${NORMAL}" >&2
exit 1
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test runner hard-fails if .just/test/fixtures/pr_bodies doesn’t exist. In this repo, .just/test isn’t present, so the script will always exit 1 if invoked. Consider skipping tests (exit 0 with a message) when fixtures are absent, consistent with .just/lib/template_sync_test.sh.

Suggested change
# Verify test directory exists
if [[ ! -d "$TEST_DIR" ]]; then
echo -e "${RED}Error: Test directory not found: $TEST_DIR${NORMAL}" >&2
exit 1
# Verify test directory exists; if absent, skip tests
if [[ ! -d "$TEST_DIR" ]]; then
echo -e "${BLUE}PR body tests skipped: test directory not found: $TEST_DIR${NORMAL}"
exit 0

Copilot uses AI. Check for mistakes.
@chicks-net chicks-net merged commit 3e2147f into main Mar 31, 2026
17 of 21 checks passed
@chicks-net chicks-net deleted the chicks/2026-03-31-gh-process-5-8 branch March 31, 2026 23:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants