Skip to content
Merged
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
100 changes: 100 additions & 0 deletions .github/workflows/upstream-sync-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Upstream Sync PR

on:
schedule:
# Every 4 hours, offset from top-of-hour GitHub Actions congestion.
- cron: '17 */4 * * *'
workflow_dispatch:

permissions:
contents: write
pull-requests: write

concurrency:
group: upstream-sync-pr
cancel-in-progress: true

jobs:
sync:
name: Create reviewed upstream sync PR
if: github.repository == 'pai-scaffolde/hermes-agent'
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
# Use the repository GitHub App credentials instead of GITHUB_TOKEN so
# branch pushes/PR updates trigger the normal pull_request CI gates.
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@7bfa3a4717ef143a604ee0a99d859b8886a96d00 # v1.9.3
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}

- name: Checkout fork main
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: main
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}

- name: Create or update upstream sync PR
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
BRANCH: automation/upstream-sync
UPSTREAM_URL: https://github.com/NousResearch/hermes-agent.git
run: |
set -euo pipefail

git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'

git remote add upstream "$UPSTREAM_URL"
git fetch origin main
git fetch upstream main

if git merge-base --is-ancestor upstream/main origin/main; then
echo "Fork main already contains upstream/main; nothing to sync."
exit 0
fi

git checkout -B "$BRANCH" origin/main
git merge --no-ff --no-edit upstream/main
Comment on lines +61 to +62

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid resetting automation/upstream-sync on every run

git checkout -B "$BRANCH" origin/main resets automation/upstream-sync to origin/main on each execution, and the subsequent git merge --no-ff --no-edit upstream/main then creates a fresh merge commit even if upstream has not moved since the last run. In the common case where an upstream-sync PR is still open, this causes branch rewrites every 4 hours (retriggering CI repeatedly) and can overwrite manual conflict-resolution commits pushed to that branch. The workflow should preserve/reuse the existing sync branch (or detect that it already contains upstream/main) instead of forcibly recreating it each run.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep sync branch available when merge conflicts occur

With set -euo pipefail, a conflict in git merge --no-ff --no-edit upstream/main aborts the step before any push or PR update happens, so maintainers get no refreshable sync branch to resolve (despite the PR policy text saying conflicts should be resolved on this branch). In any upstream-vs-fork conflict scenario, this workflow fails hard and leaves the existing PR/branch stale instead of publishing a branch that can be fixed and reviewed.

Useful? React with 👍 / 👎.


git push --force-with-lease origin "HEAD:$BRANCH"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fetch sync branch before relying on force-with-lease

The workflow force-pushes with --force-with-lease, but it only fetches origin main and never fetches origin/$BRANCH, so the lease check for automation/upstream-sync is not anchored to the latest remote-tracking value for that branch. As a result, updates pushed to the sync branch by reviewers/maintainers between runs can still be overwritten by this forced push. Fetch the branch first (or use --force-with-lease=<ref>:<expected_sha>) so the lease actually protects that ref.

Useful? React with 👍 / 👎.


cat > /tmp/upstream-sync-pr-body.md <<'EOF'
Automated upstream sync from NousResearch/hermes-agent into pai-scaffolde/hermes-agent.

Policy:
- This PR is review-gated.
- Do not auto-merge blindly.
- Existing CI/checks must pass before merging.
- If conflicts occur, resolve manually on this branch.

Source:
- upstream: NousResearch/hermes-agent main
- fork base: pai-scaffolde/hermes-agent main
EOF

PR_NUMBER="$(gh pr list \
--repo "$GITHUB_REPOSITORY" \
--head "$BRANCH" \
--base main \
Comment on lines +82 to +83

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Disambiguate PR lookup by head repository owner

gh pr list --head "$BRANCH" filters only by branch name, not by <owner>:<branch>, so this query can match open PRs from other forks that happen to use the same head ref name. In that case PR_NUMBER may target an unrelated PR and gh pr edit will update the wrong pull request instead of the automation PR. The lookup should also constrain head repository owner (e.g., via JSON fields like headRepositoryOwner) before selecting a PR number.

Useful? React with 👍 / 👎.

--state open \
--json number \
--jq '.[0].number // empty')"

if [ -n "$PR_NUMBER" ]; then
gh pr edit "$PR_NUMBER" \
--repo "$GITHUB_REPOSITORY" \
--title "chore: sync from NousResearch/hermes-agent upstream" \
--body-file /tmp/upstream-sync-pr-body.md
else
gh pr create \
--repo "$GITHUB_REPOSITORY" \
--base main \
--head "$BRANCH" \
--title "chore: sync from NousResearch/hermes-agent upstream" \
--body-file /tmp/upstream-sync-pr-body.md
fi
Loading