Skip to content
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

Add a workflow to synchronize refs from git/git to gitgitgadget/git #1

Merged
merged 1 commit into from
Aug 26, 2023
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
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
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wouldn't the rest of the code be included in this if? ie, no fetch, so no push?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@dscho Did you miss this or was I mistaken?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, sorry, I missed your comment.

No, the rest of the code still needs to run: the workflow could be triggered by a deletion, in which case source-sha would be empty.

Copy link
Collaborator

Choose a reason for hiding this comment

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

the workflow could be triggered by a deletion, in which case source-sha would be empty.

TIL on a push, an empty <src> in the refspec will delete the <dst> ref. Good to know.


# 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