Using Scheduled GitHub Actions to Sync Repos
GitHub actions are a powerful tool used for CI/CD and other development workflows. But did you know you can also run scheduled actions, similar to a cron job?
As a proof-of-concept I was curious if it would be possible to automatically keep one of my repositories in sync with another repo. For this experiment I had a branch that added a Docker configuration to Laravel projects at ractoon/laravel-docker. As Laravel released updates this branch would be missing the latest changes.
The workflow below is located at .github/workflows/laravel-sync.yml
in the ractoon/laravel-docker
repo:
name: Laravel Sync
on:
schedule:
- cron: "17 * * * *"
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set Env
run: |
echo "current_version=$(curl -Ls https://api.github.com/repos/ractoon/laravel-docker/tags | jq -r '.[0].name')" >> $GITHUB_ENV
echo "laravel_version=$(curl -Ls https://api.github.com/repos/laravel/laravel/tags | jq -r '.[0].name')" >> $GITHUB_ENV
- name: Sync Latest Laravel
run: |
echo "Checking Laravel repo..."
if [[ "${{ env.current_version }}" != "${{ env.laravel_version }}" ]]; then
curl -Ls https://api.github.com/repos/laravel/laravel/tarball -o ${{ runner.temp }}/laravel.tar
echo "Extracting and updating repo files..."
find . -maxdepth 1 ! -name .git ! -name .github ! -name .gitignore ! -name laravel.tar ! -name "docker" ! -name "cmd" ! -name "docker-compose.yml" ! -name . -exec rm -r {} \; && tar xzf ${{ runner.temp }}/laravel.tar --strip-components 1 --exclude='README.md' -C . && sed -i -e 's/^DB_HOST.*$/DB_HOST=mysql/g' ".env.example" && sed -i -e 's/^REDIS_HOST.*$/REDIS_HOST=redis/g' ".env.example"
if git show-ref --tags "${{ env.laravel_version }}" --quiet; then
echo "Tag ${{ env.laravel_version }} exists."
else
echo "Updating files to ${{ env.laravel_version }}"
git config user.name github-actions
git config user.email github-actions@github.com
git add .
if [[ `git status --porcelain` ]]; then
echo "Committing changes..."
git commit -m "Auto-update Laravel ${{ env.laravel_version }}"
git tag ${{ env.laravel_version }}
git push --atomic origin master ${{ env.laravel_version }}
else
echo "No changes to commit."
fi
fi
else
echo "Up to date."
fi
Breaking this down here's what's going on.
on:
schedule:
- cron: "17 * * * *"
Schedule this action to run at 17 minutes past the hour, every hour.
- name: Checkout
uses: actions/checkout@v2
Use the checkout action to grab the required repos.
- name: Set Env
run: |
echo "current_version=$(curl -Ls https://api.github.com/repos/ractoon/laravel-docker/tags | jq -r '.[0].name')" >> $GITHUB_ENV
echo "laravel_version=$(curl -Ls https://api.github.com/repos/laravel/laravel/tags | jq -r '.[0].name')" >> $GITHUB_ENV
Set a couple environment variables for my repos current tagged version, and the current tagged version from the Laravel repo.
- name: Sync Latest Laravel
run: |
echo "Checking Laravel repo..."
if [[ "${{ env.current_version }}" != "${{ env.laravel_version }}" ]]; then
...
else
echo "Up to date."
fi
Check if the latest Laravel version is different from my repo. If it's the same we're up to date. If not we need to do some additional work.
curl -Ls https://api.github.com/repos/laravel/laravel/tarball -o ${{ runner.temp }}/laravel.tar
echo "Extracting and updating repo files..."
find . -maxdepth 1 ! -name .git ! -name .github ! -name .gitignore ! -name laravel.tar ! -name "docker" ! -name "cmd" ! -name "docker-compose.yml" ! -name . -exec rm -r {} \; && tar xzf ${{ runner.temp }}/laravel.tar --strip-components 1 --exclude='README.md' -C . && sed -i -e 's/^DB_HOST.*$/DB_HOST=mysql/g' ".env.example" && sed -i -e 's/^REDIS_HOST.*$/REDIS_HOST=redis/g' ".env.example"
First we need to download the Laravel repo files and extract them. Then perform some cleanup on the files retrieved so we can preserve our changes, including adding Docker and some configuration.
if git show-ref --tags "${{ env.laravel_version }}" --quiet; then
echo "Tag ${{ env.laravel_version }} exists."
else
...
fi
If this repo already has a tagged release matching that of the latest Larvel version, then don't do anything. Otherwise we need to update the files.
echo "Updating files to ${{ env.laravel_version }}"
git config user.name github-actions
git config user.email github-actions@github.com
git add .
Set the git configuration and add changes to be committed.
if [[ `git status --porcelain` ]]; then
echo "Committing changes..."
git commit -m "Auto-update Laravel ${{ env.laravel_version }}"
git tag ${{ env.laravel_version }}
git push --atomic origin master ${{ env.laravel_version }}
else
echo "No changes to commit."
fi
The --porcelain
flag here makes the output easier to parse in scripts, which makes it easier to use in the boolean condition here. If there are changes to commit then they are committed here with a message saying which version of Laravel the updates belong to. A tag is added to the repo for the current Laravel version to keep things matched up. Then changes are pushed to the repo.