TO-DROP: temporarily trigger on a push #1
Workflow file for this run
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-git-gui-branches | |
on: | |
schedule: | |
- cron: '31 22 * * *' | |
workflow_dispatch: | |
push: | |
env: | |
SOURCE_REPOSITORY: prati0100/git-gui | |
TARGET_REPOSITORY: gitgitgadget/git | |
TARGET_REF_NAMESPACE: git-gui/ | |
# Ideally, we would want to limit queuing to a single workflow run (i.e. if | |
# there is already an active workflow run and a queued one, don't queue another | |
# one), but that's not supported. | |
concurrency: | |
group: ${{ github.workflow }} | |
jobs: | |
sync-git-gui-branches: | |
runs-on: ubuntu-latest | |
steps: | |
- name: check which refs need to be synchronized | |
uses: actions/github-script@v6 | |
id: check | |
with: | |
script: | | |
const sleep = async (milliseconds) => { | |
return new Promise(resolve => setTimeout(resolve, milliseconds)) | |
} | |
const getRefs = async (repository, stripRefsPrefix) => { | |
for (let i = 1; i <= 10; i++) { | |
try { | |
const [owner, repo] = repository.split('/') | |
let { data } = await github.rest.git.listMatchingRefs({ | |
owner, | |
repo, | |
ref: 'heads/' | |
}) | |
data = data.filter(e => { | |
if (!e.ref.startsWith('refs/heads/')) return false | |
e.name = e.ref.slice(11) | |
return true | |
}) | |
if (stripRefsPrefix) { | |
data = data.filter(e => { | |
if (!e.name.startsWith(stripRefsPrefix)) return false | |
e.name = e.name.slice(stripRefsPrefix.length) | |
return true | |
}) | |
} | |
return data | |
.sort((a, b) => a.ref.localeCompare(b.ref)) | |
} catch (e) { | |
if (e?.status !== 502) throw e | |
} | |
const seconds = i * i + 15 * Math.random() | |
core.info(`Encountered a Server Error; retrying in ${seconds} seconds`) | |
await sleep(1000 * seconds) | |
} | |
} | |
const sourceRefs = await getRefs(process.env.SOURCE_REPOSITORY) | |
const targetRefs = await getRefs(process.env.TARGET_REPOSITORY, process.env.TARGET_REF_NAMESPACE) | |
const targetPrefix = `refs/heads/${process.env.TARGET_REF_NAMESPACE}` | |
const refspecs = [] | |
const toFetch = new Set() | |
for (let i = 0, j = 0; i < sourceRefs.length || j < targetRefs.length; ) { | |
const c = i >= sourceRefs.length | |
? +1 | |
: j >= targetRefs.length | |
? -1 | |
: sourceRefs[i].name.localeCompare(targetRefs[j].name) | |
if (c > 0) { | |
// no source ref => delete target ref | |
refspecs.push(`:${targetPrefix}${targetRefs[j++].name}`) | |
} else if (c < 0) { | |
// no corresponding target ref yet => push source ref (new) | |
const sha = sourceRefs[i].object.sha | |
toFetch.add(sha) | |
refspecs.push(`${sha}:${targetPrefix}${sourceRefs[i++].name}`) | |
} else if (c === 0 && sourceRefs[i].object.sha !== targetRefs[j].object.sha) { | |
// target ref needs updating | |
const sha = sourceRefs[i].object.sha | |
toFetch.add(sha) | |
refspecs.push(`+${sha}:${targetPrefix}${sourceRefs[i++].name}`) | |
j++ | |
} else { | |
i++ | |
j++ | |
} | |
} | |
core.setOutput('refspec', refspecs.join(' ')) | |
targetRefs.forEach((e) => toFetch.delete(e.object.sha)) | |
core.setOutput('to-fetch', [...toFetch].join(' ')) | |
- name: obtain installation token | |
if: steps.check.outputs.refspec != '' | |
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.refspec != '' | |
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.refspec != '' | |
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 | |
# fetch some commits | |
printf '%s' '${{ steps.check.outputs.to-fetch }}' | | |
xargs -d ' ' -r git fetch --depth 10000 source | |
rm -f .git/shallow | |
# push the commits | |
printf '%s' '${{ steps.check.outputs.refspec }}' | | |
xargs -d ' ' -r git -c http.extraHeader='${{ steps.auth.outputs.header }}' \ | |
push "${{ github.server_url }}/$TARGET_REPOSITORY" |