-
Notifications
You must be signed in to change notification settings - Fork 153
Copy checkpoints from forks on PR merge via actions #259
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,191 @@ | ||||||||||
| name: 'Sync Fork Checkpoints' | ||||||||||
| description: 'Import Entire session checkpoint data from a fork after a PR is merged' | ||||||||||
|
|
||||||||||
| inputs: | ||||||||||
| token: | ||||||||||
| description: 'GitHub token with contents:write permission. Defaults to GITHUB_TOKEN.' | ||||||||||
| required: false | ||||||||||
| default: ${{ github.token }} | ||||||||||
|
|
||||||||||
| outputs: | ||||||||||
| imported_count: | ||||||||||
| description: 'Number of checkpoint commits cherry-picked' | ||||||||||
| value: ${{ steps.sync.outputs.imported_count }} | ||||||||||
| synced: | ||||||||||
| description: 'Whether any checkpoints were synced (true/false)' | ||||||||||
| value: ${{ steps.sync.outputs.synced }} | ||||||||||
|
|
||||||||||
| runs: | ||||||||||
| using: 'composite' | ||||||||||
| steps: | ||||||||||
| - name: Sync fork checkpoints | ||||||||||
| id: sync | ||||||||||
| shell: bash | ||||||||||
| env: | ||||||||||
| GH_TOKEN: ${{ inputs.token }} | ||||||||||
| REPO: ${{ github.repository }} | ||||||||||
| FORK_URL: ${{ github.event.pull_request.head.repo.clone_url }} | ||||||||||
| FORK_FULL_NAME: ${{ github.event.pull_request.head.repo.full_name }} | ||||||||||
| IS_FORK: ${{ github.event.pull_request.head.repo.fork }} | ||||||||||
| MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }} | ||||||||||
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | ||||||||||
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | ||||||||||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||||||||||
| run: | | ||||||||||
| set -euo pipefail | ||||||||||
|
|
||||||||||
| BRANCH="entire/checkpoints/v1" | ||||||||||
| ORIGIN_URL="https://x-access-token:${GH_TOKEN}@github.com/${REPO}.git" | ||||||||||
|
|
||||||||||
| echo "synced=false" >> "$GITHUB_OUTPUT" | ||||||||||
| echo "imported_count=0" >> "$GITHUB_OUTPUT" | ||||||||||
|
|
||||||||||
| # --- Guard: only run for fork PRs --- | ||||||||||
| if [ "$IS_FORK" != "true" ]; then | ||||||||||
| echo "PR is not from a fork. Skipping." | ||||||||||
| exit 0 | ||||||||||
| fi | ||||||||||
|
|
||||||||||
| # --- Set up a minimal repo (no full checkout, only checkpoints branch) --- | ||||||||||
| WORKDIR=$(mktemp -d) | ||||||||||
|
||||||||||
| WORKDIR=$(mktemp -d) | |
| WORKDIR=$(mktemp -d) | |
| trap 'rm -rf "$WORKDIR"' EXIT |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shallow fetch depth of 100 commits may not be sufficient for long-lived PRs or repositories with many commits. If the BASE_SHA is not within the last 100 commits, git log will fail silently (due to || true), and no checkpoint trailers will be found. Consider using a deeper fetch depth or fetching without depth limitation for the merge range. Alternatively, document this limitation in the action description.
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fallback logic for squash merges fetches from the fork using HEAD_SHA, but the depth of 50 commits may not be sufficient for long-lived PRs. If the BASE_SHA is not within the last 50 commits from HEAD_SHA, the git log range will be incorrect. Consider either using a deeper fetch or removing the depth limitation for this fallback case to ensure all commits in the PR are examined.
| git fetch "$FORK_URL" "$HEAD_SHA" --depth=50 2>/dev/null || true | |
| git fetch "$FORK_URL" "$HEAD_SHA" 2>/dev/null || true |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The email address uses the generic github-actions bot email. While this is functional, consider using a more specific identity that indicates this is an automated checkpoint sync operation. This would make it clearer in the git history who created these commits. For example: "github-actions[bot]" with a comment in the commit message indicating it's from the sync-fork-checkpoints action.
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git config user.name "sync-fork-checkpoints[bot]" | |
| git config user.email "sync-fork-checkpoints[bot]@users.noreply.github.com" |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When creating a new orphan branch (lines 143-146), the script creates an empty initial commit. However, if this is the first time the checkpoints branch is being created and there are valid checkpoints to import from a fork, the resulting branch history will start with an empty commit followed by the cherry-picked commits. Consider checking if there are checkpoints to import before creating the initial empty commit, or document that the empty commit serves as a branch initialization marker.
| git commit --allow-empty -m "Initialize $BRANCH" |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cherry-pick logic silently skips commits that fail to apply (lines 157-159). While this prevents the entire sync from failing, it means some checkpoint data might be lost without clear indication to users. Consider logging which commits were skipped to the action summary or output, so that users can investigate failed cherry-picks. You could collect skipped commit hashes in an array and display them at the end.
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The retry mechanism uses merge as a fallback after rebase fails, but it doesn't check if the merge itself was successful. If the merge fails, the script will continue to the next retry attempt without cleaning up the failed merge state. Add error handling after the merge command to ensure it succeeded: git merge "origin/$BRANCH" --no-edit || { echo "Merge failed"; git merge --abort 2>/dev/null || true; }
| git merge "origin/$BRANCH" --no-edit | |
| git merge "origin/$BRANCH" --no-edit || { echo "Merge failed"; git merge --abort 2>/dev/null || true; } |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The script initializes outputs at the start (lines 40-41) and updates them on successful sync (lines 174-175), but there's a logic issue: when the script exits early due to errors or no checkpoints found, the outputs remain at their default values (synced=false, imported_count=0). However, line 190 uses exit 1 for failure but doesn't update the outputs. This means consumers of this action can't distinguish between "no checkpoints to sync" (successful exit 0) and "push failed after retries" (exit 1) by checking the outputs alone. Consider setting an error output or ensuring the exit status is the primary indicator of failure.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,32 @@ | ||||||
| # Sync Entire session checkpoints from fork PRs. | ||||||
| # | ||||||
| # When a PR from a fork is merged, this workflow imports the checkpoint data | ||||||
| # (session transcripts, prompts, context) from the fork's entire/checkpoints/v1 | ||||||
|
||||||
| # branch into the upstream repo's entire/checkpoints/v1 branch. | ||||||
| # | ||||||
| # This enables the full Entire session history to be preserved even when | ||||||
| # contributors work from forks without push access to upstream. | ||||||
| # | ||||||
| # How it works: | ||||||
| # 1. Finds Entire-Checkpoint trailers in the merged commits | ||||||
| # 2. Fetches the fork's entire/checkpoints/v1 branch | ||||||
| # 3. Selectively imports only the referenced checkpoint directories | ||||||
| # 4. Pushes the updated checkpoints branch to upstream | ||||||
|
|
||||||
| name: Sync Fork Checkpoints | ||||||
|
|
||||||
| on: | ||||||
| pull_request_target: | ||||||
| types: [closed] | ||||||
|
Comment on lines
+19
to
+20
|
||||||
|
|
||||||
| permissions: | ||||||
| contents: write | ||||||
|
|
||||||
| jobs: | ||||||
| sync-checkpoints: | ||||||
| if: >- | ||||||
| github.event.pull_request.merged == true && | ||||||
| github.event.pull_request.head.repo.fork == true | ||||||
| runs-on: ubuntu-latest | ||||||
| steps: | ||||||
| - uses: entireio/cli/.github/actions/sync-fork-checkpoints@main | ||||||
|
||||||
| - uses: entireio/cli/.github/actions/sync-fork-checkpoints@main | |
| - uses: ./.github/actions/sync-fork-checkpoints |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The workflow is missing a required checkout step. Since the action being called is a local composite action (stored in .github/actions/sync-fork-checkpoints/), GitHub Actions needs to checkout the repository first to access the action definition. Add - uses: actions/checkout@v6 as the first step before calling the action. Without this, the workflow will fail because the action code won't be available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The script accepts FORK_URL from untrusted input (github.event.pull_request.head.repo.clone_url). While there's validation that IS_FORK is true, malicious fork URLs could potentially be crafted to exploit git operations. Consider adding validation to ensure the FORK_URL has an expected format (e.g., matches https://github.com/* pattern) before using it in git fetch commands to prevent potential security issues.