From 520d9a945c57c215d62d246e21237b0187e4d6ac Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Wed, 14 Jan 2026 00:54:46 +0530 Subject: [PATCH] chore: add workflow files to git tracking --- .gitignore | 2 - DEVELOPMENT_WORKFLOW.md | 253 ++++++++++++++++++++++++++++++++++++++++ check-sync.sh | 215 ++++++++++++++++++++++++++++++++++ 3 files changed, 468 insertions(+), 2 deletions(-) create mode 100644 DEVELOPMENT_WORKFLOW.md create mode 100755 check-sync.sh diff --git a/.gitignore b/.gitignore index be8843e0..b595045a 100644 --- a/.gitignore +++ b/.gitignore @@ -90,8 +90,6 @@ pnpm-lock.yaml yarn.lock # Fork-specific workflow files (should never be committed) -DEVELOPMENT_WORKFLOW.md -check-sync.sh # API key files data/.api-key data/credentials.json diff --git a/DEVELOPMENT_WORKFLOW.md b/DEVELOPMENT_WORKFLOW.md new file mode 100644 index 00000000..0ce198ce --- /dev/null +++ b/DEVELOPMENT_WORKFLOW.md @@ -0,0 +1,253 @@ +# Development Workflow + +This document defines the standard workflow for keeping a branch in sync with the upstream +release candidate (RC) and for shipping feature work. It is paired with `check-sync.sh`. + +## Quick Decision Rule + +1. Ask the user to select a workflow: + - **Sync Workflow** → you are maintaining the current RC branch with fixes/improvements + and will push the same fixes to both origin and upstream RC when you have local + commits to publish. + - **PR Workflow** → you are starting new feature work on a new branch; upstream updates + happen via PR only. +2. After the user selects, run: + ```bash + ./check-sync.sh + ``` +3. Use the status output to confirm alignment. If it reports **diverged**, default to + merging `upstream/` into the current branch and preserving local commits. + For Sync Workflow, when the working tree is clean and you are behind upstream RC, + proceed with the fetch + merge without asking for additional confirmation. + +## Target RC Resolution + +The target RC is resolved dynamically so the workflow stays current as the RC changes. + +Resolution order: + +1. Latest `upstream/v*rc` branch (auto-detected) +2. `upstream/HEAD` (fallback) +3. If neither is available, you must pass `--rc ` + +Override for a single run: + +```bash +./check-sync.sh --rc +``` + +## Pre-Flight Checklist + +1. Confirm a clean working tree: + ```bash + git status + ``` +2. Confirm the current branch: + ```bash + git branch --show-current + ``` +3. Ensure remotes exist (origin + upstream): + ```bash + git remote -v + ``` + +## Sync Workflow (Upstream Sync) + +Use this flow when you are updating the current branch with fixes or improvements and +intend to keep origin and upstream RC in lockstep. + +1. **Check sync status** + ```bash + ./check-sync.sh + ``` +2. **Update from upstream RC before editing (no pulls)** + - **Behind upstream RC** → fetch and merge RC into your branch: + ```bash + git fetch upstream + git merge upstream/ --no-edit + ``` + When the working tree is clean and the user selected Sync Workflow, proceed without + an extra confirmation prompt. + - **Diverged** → stop and resolve manually. +3. **Resolve conflicts if needed** + - Handle conflicts intelligently: preserve upstream behavior and your local intent. +4. **Make changes and commit (if you are delivering fixes)** + ```bash + git add -A + git commit -m "type: description" + ``` +5. **Build to verify** + ```bash + npm run build:packages + npm run build + ``` +6. **Push after a successful merge to keep remotes aligned** + - If you only merged upstream RC changes, push **origin only** to sync your fork: + ```bash + git push origin + ``` + - If you have local fixes to publish, push **origin + upstream**: + ```bash + git push origin + git push upstream : + ``` + - Always ask the user which push to perform. + - Origin (origin-only sync): + ```bash + git push origin + ``` + - Upstream RC (publish the same fixes when you have local commits): + ```bash + git push upstream : + ``` +7. **Re-check sync** + ```bash + ./check-sync.sh + ``` + +## PR Workflow (Feature Work) + +Use this flow only for new feature work on a new branch. Do not push to upstream RC. + +1. **Create or switch to a feature branch** + ```bash + git checkout -b + ``` +2. **Make changes and commit** + ```bash + git add -A + git commit -m "type: description" + ``` +3. **Merge upstream RC before shipping** + ```bash + git merge upstream/ --no-edit + ``` +4. **Build and/or test** + ```bash + npm run build:packages + npm run build + ``` +5. **Push to origin** + ```bash + git push -u origin + ``` +6. **Create or update the PR** + - Use `gh pr create` or the GitHub UI. +7. **Review and follow-up** + +- Apply feedback, commit changes, and push again. +- Re-run `./check-sync.sh` if additional upstream sync is needed. + +## Conflict Resolution Checklist + +1. Identify which changes are from upstream vs. local. +2. Preserve both behaviors where possible; avoid dropping either side. +3. Prefer minimal, safe integrations over refactors. +4. Re-run build commands after resolving conflicts. +5. Re-run `./check-sync.sh` to confirm status. + +## Build/Test Matrix + +- **Sync Workflow**: `npm run build:packages` and `npm run build`. +- **PR Workflow**: `npm run build:packages` and `npm run build` (plus relevant tests). + +## Post-Sync Verification + +1. `git status` should be clean. +2. `./check-sync.sh` should show expected alignment. +3. Verify recent commits with: + ```bash + git log --oneline -5 + ``` + +## check-sync.sh Usage + +- Uses dynamic Target RC resolution (see above). +- Override target RC: + ```bash + ./check-sync.sh --rc + ``` +- Optional preview limit: + ```bash + ./check-sync.sh --preview 10 + ``` +- The script prints sync status for both origin and upstream and previews recent commits + when you are behind. + +## Stop Conditions + +Stop and ask for guidance if any of the following are true: + +- The working tree is dirty and you are about to merge or push. +- `./check-sync.sh` reports **diverged** during PR Workflow, or a merge cannot be completed. +- The script cannot resolve a target RC and requests `--rc`. +- A build fails after sync or conflict resolution. + +## AI Agent Guardrails + +- Always run `./check-sync.sh` before merges or pushes. +- Always ask for explicit user approval before any push command. +- Do not ask for additional confirmation before a Sync Workflow fetch + merge when the + working tree is clean and the user has already selected the Sync Workflow. +- Choose Sync vs PR workflow based on intent (RC maintenance vs new feature work), not + on the script's workflow hint. +- Only use force push when the user explicitly requests a history rewrite. +- Ask for explicit approval before dependency installs, branch deletion, or destructive operations. +- When resolving merge conflicts, preserve both upstream changes and local intent where possible. +- Do not create or switch to new branches unless the user explicitly requests it. + +## AI Agent Decision Guidance + +Agents should provide concrete, task-specific suggestions instead of repeatedly asking +open-ended questions. Use the user's stated goal and the `./check-sync.sh` status to +propose a default path plus one or two alternatives, and only ask for confirmation when +an action requires explicit approval. + +Default behavior: + +- If the intent is RC maintenance, recommend the Sync Workflow and proceed with + safe preparation steps (status checks, previews). If the branch is behind upstream RC, + fetch and merge without additional confirmation when the working tree is clean, then + push to origin to keep the fork aligned. Push upstream only when there are local fixes + to publish. +- If the intent is new feature work, recommend the PR Workflow and proceed with safe + preparation steps (status checks, identifying scope). Ask for approval before merges, + pushes, or dependency installs. +- If `./check-sync.sh` reports **diverged** during Sync Workflow, merge + `upstream/` into the current branch and preserve local commits. +- If `./check-sync.sh` reports **diverged** during PR Workflow, stop and ask for guidance + with a short explanation of the divergence and the minimal options to resolve it. + If the user's intent is RC maintenance, prefer the Sync Workflow regardless of the + script hint. When the intent is new feature work, use the PR Workflow and avoid upstream + RC pushes. + +Suggestion format (keep it short): + +- **Recommended**: one sentence with the default path and why it fits the task. +- **Alternatives**: one or two options with the tradeoff or prerequisite. +- **Approval points**: mention any upcoming actions that need explicit approval (exclude sync + workflow pushes and merges). + +## Failure Modes and How to Avoid Them + +Sync Workflow: + +- Wrong RC target: verify the auto-detected RC in `./check-sync.sh` output before merging. +- Diverged from upstream RC: stop and resolve manually before any merge or push. +- Dirty working tree: commit or stash before syncing to avoid accidental merges. +- Missing remotes: ensure both `origin` and `upstream` are configured before syncing. +- Build breaks after sync: run `npm run build:packages` and `npm run build` before pushing. + +PR Workflow: + +- Branch not synced to current RC: re-run `./check-sync.sh` and merge RC before shipping. +- Pushing the wrong branch: confirm `git branch --show-current` before pushing. +- Unreviewed changes: always commit and push to origin before opening or updating a PR. +- Skipped tests/builds: run the build commands before declaring the PR ready. + +## Notes + +- Avoid merging with uncommitted changes; commit or stash first. +- Prefer merge over rebase for PR branches; rebases rewrite history and often require a force push, + which should only be done with an explicit user request. +- Use clear, conventional commit messages and split unrelated changes into separate commits. diff --git a/check-sync.sh b/check-sync.sh new file mode 100755 index 00000000..81b5863e --- /dev/null +++ b/check-sync.sh @@ -0,0 +1,215 @@ +#!/usr/bin/env bash +set -euo pipefail + +DEFAULT_RC_PATTERN="v*rc" +DEFAULT_PREVIEW_COUNT=5 + +PREVIEW_COUNT="${PREVIEW_COUNT:-$DEFAULT_PREVIEW_COUNT}" +CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)" + +ORIGIN_REF="origin/${CURRENT_BRANCH}" +TARGET_RC_SOURCE="auto" + +print_header() { + echo "=== Sync Status Check ===" + echo + printf "Target RC: %s (%s)\n" "$TARGET_RC" "$TARGET_RC_SOURCE" + echo +} + +ensure_git_repo() { + if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "Not inside a git repository." + exit 1 + fi +} + +ensure_remote() { + local remote="$1" + if ! git remote get-url "$remote" >/dev/null 2>&1; then + echo "Remote '$remote' is not configured." + exit 1 + fi +} + +fetch_remote() { + local remote="$1" + git fetch --quiet "$remote" +} + +warn_if_dirty() { + if [[ -n "$(git status --porcelain)" ]]; then + echo "Warning: working tree has uncommitted changes." + echo + fi +} + +resolve_target_rc() { + if [[ -n "${TARGET_RC:-}" ]]; then + return + fi + + local rc_candidates + rc_candidates="$(git for-each-ref --format='%(refname:short)' "refs/remotes/upstream/${DEFAULT_RC_PATTERN}" || true)" + if [[ -n "$rc_candidates" ]]; then + TARGET_RC="$(printf "%s\n" "$rc_candidates" | sed 's|^upstream/||' | sort -V | tail -n 1)" + TARGET_RC_SOURCE="auto:latest" + return + fi + + local upstream_head + upstream_head="$(git symbolic-ref --quiet --short refs/remotes/upstream/HEAD 2>/dev/null || true)" + if [[ -n "$upstream_head" ]]; then + TARGET_RC="${upstream_head#upstream/}" + TARGET_RC_SOURCE="auto:upstream-head" + return + fi + + echo "Unable to resolve target RC automatically. Use --rc ." + exit 1 +} + +ref_exists() { + local ref="$1" + git show-ref --verify --quiet "refs/remotes/${ref}" +} + +print_status_line() { + local label="$1" + local behind="$2" + local ahead="$3" + + if [[ "$behind" -eq 0 && "$ahead" -eq 0 ]]; then + printf "✅ %s: in sync (behind %s, ahead %s)\n" "$label" "$behind" "$ahead" + elif [[ "$behind" -eq 0 ]]; then + printf "⬆️ %s: ahead %s (behind %s)\n" "$label" "$ahead" "$behind" + elif [[ "$ahead" -eq 0 ]]; then + printf "⬇️ %s: behind %s (ahead %s)\n" "$label" "$behind" "$ahead" + else + printf "⚠️ %s: %s behind, %s ahead (diverged)\n" "$label" "$behind" "$ahead" + fi +} + +print_preview() { + local title="$1" + local range="$2" + + echo + echo "$title" + git log --oneline -n "$PREVIEW_COUNT" "$range" +} + +print_branch_context() { + echo "Branch: $CURRENT_BRANCH" + echo "Upstream RC: $UPSTREAM_REF" + echo "Upstream push: enabled for sync workflow" + echo +} + +print_upstream_summary() { + local behind="$1" + local ahead="$2" + + if [[ "$behind" -eq 0 && "$ahead" -eq 0 ]]; then + echo "Branch vs upstream RC: in sync (behind $behind, ahead $ahead)" + else + echo "Branch vs upstream RC: behind $behind, ahead $ahead" + fi +} + +print_workflow_hint() { + local behind="$1" + local ahead="$2" + + if [[ "$behind" -eq 0 && "$ahead" -eq 0 ]]; then + echo "Workflow: sync" + elif [[ "$behind" -gt 0 && "$ahead" -eq 0 ]]; then + echo "Workflow: sync (merge upstream RC)" + elif [[ "$ahead" -gt 0 && "$behind" -eq 0 ]]; then + echo "Workflow: pr (local work not in upstream)" + else + echo "Workflow: diverged (resolve manually)" + fi +} + +print_usage() { + echo "Usage: ./check-sync.sh [--rc ] [--preview ]" +} + +parse_args() { + while [[ $# -gt 0 ]]; do + case "$1" in + --rc) + shift + if [[ -z "${1-}" ]]; then + echo "Missing value for --rc" + exit 1 + fi + TARGET_RC="$1" + TARGET_RC_SOURCE="flag" + ;; + --preview) + shift + if [[ -z "${1-}" ]]; then + echo "Missing value for --preview" + exit 1 + fi + if ! [[ "$1" =~ ^[0-9]+$ ]]; then + echo "Invalid preview count: $1" + exit 1 + fi + PREVIEW_COUNT="$1" + ;; + -h|--help) + print_usage + exit 0 + ;; + *) + echo "Unknown argument: $1" + print_usage + exit 1 + ;; + esac + shift + done +} + +ensure_git_repo +ensure_remote origin +ensure_remote upstream +parse_args "$@" + +fetch_remote origin +fetch_remote upstream +resolve_target_rc + +UPSTREAM_REF="upstream/${TARGET_RC}" + +print_header +warn_if_dirty +print_branch_context + +if ! ref_exists "$ORIGIN_REF"; then + echo "Origin branch '$ORIGIN_REF' does not exist." +else + read -r origin_behind origin_ahead < <(git rev-list --left-right --count "$ORIGIN_REF...HEAD") + print_status_line "Origin" "$origin_behind" "$origin_ahead" +fi + +if ! ref_exists "$UPSTREAM_REF"; then + echo "Upstream ref '$UPSTREAM_REF' does not exist." +else + read -r upstream_behind upstream_ahead < <(git rev-list --left-right --count "$UPSTREAM_REF...HEAD") + print_status_line "Upstream" "$upstream_behind" "$upstream_ahead" + echo + print_upstream_summary "$upstream_behind" "$upstream_ahead" + print_workflow_hint "$upstream_behind" "$upstream_ahead" + + if [[ "$upstream_behind" -gt 0 ]]; then + print_preview "Recent upstream commits:" "HEAD..$UPSTREAM_REF" + fi + + if [[ "$upstream_ahead" -gt 0 ]]; then + print_preview "Commits on this branch not in upstream:" "$UPSTREAM_REF..HEAD" + fi +fi