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