-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a workflow to synchronize refs from git/git to gitgitgadget/git
The idea is, of course, to act on the `push` webhook that dutifully pings GitGitGadget's GitHub App every time a ref is pushed to `git/git`, and to let the App trigger this shiny new workflow to do the honors of keeping gitgitgadget/git up to date. Signed-off-by: Johannes Schindelin <[email protected]>
- Loading branch information
Showing
1 changed file
with
109 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
name: sync-ref | ||
|
||
on: | ||
workflow_dispatch: | ||
inputs: | ||
ref: | ||
description: The ref to synchronize from git/git to gitgitgadget/git | ||
type: string | ||
default: refs/heads/master | ||
source-repository: | ||
description: The repository from which to sync the ref | ||
type: string | ||
default: git/git | ||
target-repository: | ||
description: The repository to which to sync the ref | ||
type: string | ||
default: gitgitgadget/git | ||
|
||
env: | ||
SOURCE_REPOSITORY: ${{ inputs.source-repository || 'git/git' }} | ||
TARGET_REPOSITORY: ${{ inputs.target-repository || 'gitgitgadget/git' }} | ||
REF: ${{ inputs.ref || 'refs/heads/master' }} | ||
|
||
jobs: | ||
sync-ref: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: check whether the ref is in sync | ||
uses: actions/github-script@v6 | ||
id: check | ||
with: | ||
script: | | ||
const getSHA = async (repository, ref) => { | ||
if (ref.startsWith('refs/heads/') || ref.startsWith('refs/tags/')) ref = ref.substring(4) | ||
else throw new Error(`Cannot handle ref '${ref}`) | ||
try { | ||
const [owner, repo] = repository.split('/') | ||
const { data: { object: { sha } } } = await github.rest.git.getRef({ | ||
owner, | ||
repo, | ||
ref | ||
}) | ||
return sha | ||
} catch (e) { | ||
if (e?.status == 404) return undefined | ||
throw e | ||
} | ||
} | ||
const sourceSHA = await getSHA(process.env.SOURCE_REPOSITORY, process.env.REF) | ||
const targetSHA = await getSHA(process.env.TARGET_REPOSITORY, process.env.REF) | ||
// skip sync if SHAs match, making extra certain that `master` is also synced to `main` | ||
const skip = sourceSHA !== targetSHA | ||
? false | ||
: (process.env.REF !== 'refs/heads/master' || | ||
sourceSHA === await getSHA(process.env.TARGET_REPOSITORY, 'refs/heads/main')) | ||
core.setOutput('skip', skip ? 'true' : 'false') | ||
core.setOutput('source-sha', sourceSHA || '') | ||
core.setOutput('target-sha', targetSHA || '') | ||
- name: obtain installation token | ||
if: steps.check.outputs.skip == 'false' | ||
uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 | ||
id: token | ||
with: | ||
app_id: ${{ secrets.GITGITGADGET_GITHUB_APP_ID }} | ||
private_key: ${{ secrets.GITGITGADGET_GITHUB_APP_PRIVATE_KEY }} | ||
repository: ${{ env.TARGET_REPOSITORY }} | ||
- name: set authorization header | ||
if: steps.check.outputs.skip == 'false' | ||
uses: actions/github-script@v6 | ||
id: auth | ||
with: | ||
script: | | ||
// Sadly, `git push` does not work with 'Authorization: Bearer <PAT>', therefore | ||
// we have to use the `Basic` variant | ||
const auth = Buffer.from('PAT:${{ steps.token.outputs.token }}').toString('base64') | ||
core.setSecret(auth) | ||
core.setOutput('header', `Authorization: Basic ${auth}`) | ||
- name: sync | ||
if: steps.check.outputs.skip == 'false' | ||
shell: bash | ||
run: | | ||
set -ex | ||
git init --bare | ||
git remote add source "${{ github.server_url }}/$SOURCE_REPOSITORY" | ||
# pretend to be a partial clone | ||
git config remote.source.promisor true | ||
git config remote.source.partialCloneFilter blob:none | ||
if test -n "${{ steps.check.outputs.source-sha }}" | ||
then | ||
# fetch some commits | ||
git fetch --depth 10000 source ${{ steps.check.outputs.source-sha }} | ||
rm -f .git/shallow | ||
fi | ||
# push the commits | ||
extra= | ||
case "$REF" in | ||
refs/heads/master) force=; extra="${{ steps.check.outputs.source-sha }}:refs/heads/main";; | ||
refs/heads/main|refs/heads/maint|refs/heads/maint-*) force=;; | ||
*) force=--force;; | ||
esac | ||
git -c http.extraHeader='${{ steps.auth.outputs.header }}' \ | ||
push $force \ | ||
"${{ github.server_url }}/$TARGET_REPOSITORY" \ | ||
"${{ steps.check.outputs.source-sha }}:$REF" $extra |