sync-ref #29
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
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 |