From 269d077423fa59fee30edc536c7660c98c853e33 Mon Sep 17 00:00:00 2001 From: Thomas Cooper Date: Wed, 3 Jan 2024 23:44:46 -0500 Subject: [PATCH] sync remote repos outside a workflow ctx * add a bunch of new inputs into the action to allow a remote repo/host to be used as the upstream or destination of the sync. * the new API surface on the action isn't the best but should be compatible with the existing usage (relying on running the workflow on the source repo and read in the builtin env vars from GitHub Actions) related: #114 --- README.md | 40 ++++++++++++++++++++++++++++++++++++++-- action.yml | 28 +++++++++++++++++++++++++--- github-sync.sh | 50 ++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 103 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 924e96ddf..7f429c540 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,12 @@ [![All Contributors](https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square)](#contributors-) -A GitHub Action for syncing the current repository using **force push**. +A GitHub Action for syncing two repositories using **force push**. ## Features * Sync branches between two GitHub repositories - * Sync branches from a remote repository + * Sync branches from one remote repository to another * GitHub action can be triggered on a timer or on push * To push to a remote repository, please checkout [git-sync](https://github.com/marketplace/actions/git-sync-action) * Support syncing tags. @@ -16,6 +16,9 @@ A GitHub Action for syncing the current repository using **force push**. ## Usage +This action operates in two "modes", a local mode (using the current GitHub repository as a source of truth where the workflow is running) or remote mode to sync two repositories outside of the workflow repo. + +### Local mode Create a personal access token and add to repository's secret as `PAT` ### GitHub Actions @@ -44,6 +47,39 @@ jobs: ``` If `source_repo` is private or with another provider, either (1) use an authenticated HTTPS repo clone url like `https://${access_token}@github.com/owner/repository.git` or (2) set a `SSH_PRIVATE_KEY` secret environment variable and use the SSH clone url +### Remote mode +Set the requisite secrets for syncing two remote repositories on the GitHub repository that will execute this action. +``` +# File: .github/workflows/repo-sync.yml +on: + schedule: + - cron: "*/15 * * * *" + workflow_dispatch: + +jobs: + repo-sync: + runs-on: ubuntu-latest + steps: + - env: + SOURCE_TOKEN: ${{ secrets.SOURCE_TOKEN }} + run: | + git clone https://$SOURCE_TOKEN@/user/foo-repo.git foo-repo/ + - name: repo-sync + uses: repo-sync/github-sync@v2 + working-directory: ./foo-repo + with: + source_repo: "user1/foo-repo" + source_host: github.com # If omitted, defaults to github.com + source_token: ${{ secrets.SOURCE_TOKEN }} + source_user: "user1" + source_branch: "main" + destination_repo: "user2/bar-repo" + destination_host: gitlab.com + destination_user: oauth2 + destination_token: ${{ secrets.DESTINATION_TOKEN }} + destination_branch: "main" +``` + ### Workflow overwriting If `destination_branch` and the branch where you will create this workflow will be the same, The workflow (and all files) will be overwritten by `source_branch` files. A potential solution is: Create a new branch with the actions file and make it the default branch. diff --git a/action.yml b/action.yml index ecec95d61..b45239200 100644 --- a/action.yml +++ b/action.yml @@ -1,6 +1,6 @@ name: GitHub Repo Sync author: Wei He -description: ⤵️ Sync current repository with remote +description: ⤵️ Sync two repositories using GitHub Actions branding: icon: 'git-branch' color: 'gray-dark' @@ -14,9 +14,24 @@ inputs: destination_branch: description: Branch name to sync to in this repo required: true + source_host: + description: Hostname of the source repo if different from github.com. If set, the source_repo must be repo slug. + required: false + destination_host: + description: Hostname of the destination repo if different from github.com + required: false + destination_repo: + description: Git repo slug. If not provided, the source_repo will be used. + required: false + destination_user: + description: GitHub username for pushing to the destination repo. If not provided, the default GITHUB_ACTOR will be used. + required: false + destination_token: + description: GitHub token secret for pushing to the destination repo. If not provided, the default GITHUB_TOKEN will be used. + required: false github_token: - description: GitHub token secret - required: true + description: GitHub token secret for accessing the source repo. + required: false sync_tags: description: Should tags also be synced required: false @@ -26,6 +41,13 @@ runs: env: GITHUB_TOKEN: ${{ inputs.github_token }} SYNC_TAGS: ${{ inputs.sync_tags }} + SOURCE_HOST: ${{ inputs.source_host }} + SOURCE_TOKEN: ${{ inputs.github_token }} + SOURCE_USER: ${{ github.actor }} + DESTINATION_HOST: ${{ inputs.destination_host }} + DESTINATION_USER: ${{ inputs.destination_user }} + DESTINATION_TOKEN: ${{ inputs.destination_token }} + DESTINATION_REPO: ${{ inputs.destination_repo }} args: - ${{ inputs.source_repo }} - ${{ inputs.source_branch }}:${{ inputs.destination_branch }} diff --git a/github-sync.sh b/github-sync.sh index bca34ef58..57c4c3442 100755 --- a/github-sync.sh +++ b/github-sync.sh @@ -2,24 +2,30 @@ set -e +# It's not recommended to use DEBUG=1 in production environments as it will leak your credentials. +if [ -n "$DEBUG" ]; then + set -x +fi + UPSTREAM_REPO=$1 BRANCH_MAPPING=$2 -if [[ -z "$UPSTREAM_REPO" ]]; then +if [ -z "$UPSTREAM_REPO" ]; then echo "Missing \$UPSTREAM_REPO" exit 1 fi -if [[ -z "$BRANCH_MAPPING" ]]; then +if [ -z "$BRANCH_MAPPING" ]; then echo "Missing \$SOURCE_BRANCH:\$DESTINATION_BRANCH" exit 1 fi -if ! echo $UPSTREAM_REPO | grep -Eq ':|@|\.git\/?$' +if ! echo "$UPSTREAM_REPO" | grep -Eq ':|@|\.git/?$' then - echo "UPSTREAM_REPO does not seem to be a valid git URI, assuming it's a GitHub repo" + echo "UPSTREAM_REPO does not seem to be a valid git URI, assuming it's a GitHub repo slug" echo "Originally: $UPSTREAM_REPO" - UPSTREAM_REPO="https://github.com/${UPSTREAM_REPO}.git" + _src_hostname=${SOURCE_HOST:-"github.com"} + UPSTREAM_REPO="https://${_src_hostname}/${UPSTREAM_REPO}.git" echo "Now: $UPSTREAM_REPO" fi @@ -28,11 +34,35 @@ echo "BRANCHES=$BRANCH_MAPPING" git config --unset-all http."https://github.com/".extraheader || : -echo "Resetting origin to: https://$GITHUB_ACTOR:$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY" -git remote set-url origin "https://$GITHUB_ACTOR:$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY" +_hostname=${DESTINATION_HOST:-"github.com"} +_user=${DESTINATION_USER:-$GITHUB_ACTOR} +_token=${DESTINATION_TOKEN:-$GITHUB_TOKEN} +_repo=${DESTINATION_REPO:-$GITHUB_REPOSITORY} + +if [ -z "$_user" ]; then + echo "Missing \$DESTINATION_USER or \$GITHUB_ACTOR" + exit 1 +fi + +if [ -z "$_token" ]; then + echo "Missing \$DESTINATION_TOKEN or \$GITHUB_TOKEN" + exit 1 +fi + +if [ -z "$_repo" ]; then + echo "Missing \$DESTINATION_REPO or \$GITHUB_REPOSITORY" + exit 1 +fi + +echo "Resetting origin to: https://$_user:***@$_hostname/$_repo" +git remote set-url origin "https://$_user:$_token@$_hostname/$_repo" echo "Adding tmp_upstream $UPSTREAM_REPO" -git remote add tmp_upstream "$UPSTREAM_REPO" +if [ -n "${SOURCE_USER}" ] && [ -n "${SOURCE_TOKEN}" ]; then + git remote add tmp_upstream "https://${SOURCE_USER}:${SOURCE_TOKEN}@${UPSTREAM_REPO#https://}" +else + git remote add tmp_upstream "$UPSTREAM_REPO" +fi echo "Fetching tmp_upstream" git fetch tmp_upstream --quiet @@ -41,9 +71,9 @@ git remote --verbose echo "Pushing changings from tmp_upstream to origin" git push origin "refs/remotes/tmp_upstream/${BRANCH_MAPPING%%:*}:refs/heads/${BRANCH_MAPPING#*:}" --force -if [[ "$SYNC_TAGS" = true ]]; then +if [ "$SYNC_TAGS" = true ]; then echo "Force syncing all tags" - git tag -d $(git tag -l) > /dev/null + git tag -d "$(git tag -l)" > /dev/null git fetch tmp_upstream --tags --quiet git push origin --tags --force fi