Automating Frontend Workflows with custom webhooks in GitHub Actions
Complete Guide to Automated CI/CD Pipelines for Static Sites and Modern Web Apps

Managing content separately from your website’s codebase can be beneficial for security, organization, and collaboration. In this article, I’ll walk you through how I set up GitHub Actions to automatically pull content from a private repository and rebuild my website whenever changes are made.
Overview
The setup in the example for this tutorial involves:
- A public repository containing the website code.
- A private repository containing sensitive content/data. Add this as a submodule to your public repository.
- GitHub Actions workflow that triggers a workflow in my website’s repository whenever my private content repository is updated.
- Proper authentication and security measures.
Repository Structure
Main Website Repository (Public)
rishikc.com/
├── .github/workflows/
│ └── deploy.yml
│ └── update-content.yml
├── content-repo/
...
Content Repository (Private)
content-repo/
├── .github/workflows/
│ └── trigger-update.yml
├── articles/
└── assets/
└── etc/
└── etc/
Setting Up Authentication
Create a Personal Access Token
First, you’ll need to create a Personal Access Token (PAT) with appropriate permissions:
- Go to GitHub Settings > Developer settings > Personal access tokens.
- Click “Generate new token”. (I used fine-grained tokens, but you can use classic tokens if you are comfortable with that option)
- Select permissions (for private repository access).
- Copy the generated token.
For the setup I have in my GitHub repositories, I used :
- A token with write access to my public repository. This is used in the workflow for my private content repository so that it can dispatch the event.
- A token with access to my public and private repository. This is used in the workflow for my public repository for the website so that it can pull in the latest commits from the private repository.
- A token that has only read access to my website and content repositories. This is used for the build and deployment workflow.
This way, I never have a token with any excess permissions than what is required for that particular workflow. Best practices for the win!
For this tutorial, I assume you are setting up your repositories similarly.
Add Repository Secrets
In your website’s public repository:
- Go to Settings > Secrets and variables > Actions.
- Add the following secrets:
CONTENT_UPDATE_PAT
: Your Personal Access Token (or PAT) with access to pull in the latest content from the private repository and push the changes in your public repository.READ_ONLY_PAT
: Your Personal Access Token with access to read the content of your website’s public repository to pull the latest changes, build and deploy the website.
In your content’s repository:
- Go to Settings > Secrets and variables > Actions.
- Add the following secrets:
EVENT_TRIGGER_PAT
: Your Personal Access Token (or PAT) with access to dispatch an event for the website’s repository to trigger the workflow that will pull in the latest commit from this private repository.
GitHub Actions Workflow
Here’s the foundation of the workflow that pulls content and deploys the site.
Build and Deployment Workflow Setup
First, we set up a build and deployment workflow for our website.
name: Build and Deploy
on:
# Trigger the workflow every time you push to the `main` branch
push:
branches: [ main ]
# Allows you to run this workflow manually from the Actions tab on GitHub.
workflow_dispatch:
# Allow this job to clone the repo and create a page deployment
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout your repository using git
uses: actions/checkout@v4
with:
submodules: true
token: ${{ secrets.READ_ONLY_PAT }}
persist-credentials: false
# Additional build and deploy steps...
Workflow to Pull from Private Repository
Create another workflow in your website’s repository that will be triggered whenever an event is dispatched by the private repository:
name: Update Content Submodule and Trigger Deployment
on:
repository_dispatch:
types:
- content-update
jobs:
update-content-submodule:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: true
token: ${{ secrets.CONTENT_UPDATE_PAT }}
persist-credentials: false
- name: Authorize Git
run: |
git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com"
git config --global user.name "$GITHUB_ACTOR"
git config --global url."https://${{ secrets.CONTENT_UPDATE_PAT }}@github.com/".insteadOf "https://github.com/"
- name: Update content submodule
run: |
git submodule sync
git submodule update --remote --recursive
git add content
git commit -m "[github action] auto update content submodule" || echo "No changes to commit"
git push origin main
Triggering Builds from Content Repository
Now, we will set up our private repository to dispatch an event that will trigger the workflow in our public repository.
Webhook Setup
To automatically trigger builds when content changes, set up a webhook in your private repository:
# .github/workflows/notify-website.yml in content repository
name: Trigger Main Site Update
on:
push:
branches:
- main
# Allows you to run this workflow manually from the Actions tab on GitHub.
workflow_dispatch:
jobs:
dispatch:
runs-on: ubuntu-latest
steps:
- name: Dispatch update to site
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.EVENT_TRIGGER_PAT }}
repository: rishichawda/rishikc.com # <your-account/repo-name>
event-type: content-update # same as the value in `repository_dispatch` in the workflow of your public repository
That’s all we need to do. It is that simple!
When your private repository is updated, it dispatches an event that triggers the private submodule update in your public repository. Since our website’s repository has been configured to run the deployment whenever there are any changes in its main branch, the deployment workflow will automatically update our website.
Security Considerations
Principle of Least Privilege
- Use fine-grained PATs when possible.
- Limit token scope to only necessary permissions.
- Regularly rotate access tokens (Do not use the no-expiration option).
Environment Variables
- Never hardcode sensitive information.
- Use GitHub Secrets for all authentication tokens.
- Consider using GitHub’s OIDC for enhanced security.
Troubleshooting Common Issues
Authentication Errors
- Verify PAT permissions and expiration.
- Check repository names and owner settings.
- Ensure secrets are properly configured.
Build Failures
- Check Node.js version compatibility.
- Verify content structure and dependencies.
- Review build logs for specific errors.
Conclusion
This setup provides a robust, automated pipeline for managing content separately from your website code while maintaining security and efficiency. The workflow ensures your site stays updated whenever content changes, providing a seamless experience for content management and deployment.
The key benefits include:
- Separation of concerns between code and content.
- Automated deployment pipeline.
- Enhanced security for sensitive content.
- Scalable architecture for multiple content sources.
With this foundation, you can extend the workflow to include additional features like content validation, multiple environments, and advanced deployment strategies.
More importantly, have fun automating!