Skip to content

Commit

Permalink
Add a workflow to synchronize refs from git/git to gitgitgadget/git
Browse files Browse the repository at this point in the history
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
dscho committed Aug 22, 2023
1 parent d9544c8 commit e84a016
Showing 1 changed file with 109 additions and 0 deletions.
109 changes: 109 additions & 0 deletions .github/workflows/sync-ref.yml
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

0 comments on commit e84a016

Please sign in to comment.