Back to articles

Automating Frontend Workflows with custom webhooks in GitHub Actions

Complete Guide to Automated CI/CD Pipelines for Static Sites and Modern Web Apps

May 23, 2025
GitHub Actions workflow for frontend projects
automation
frontend
tutorial
javascript
web development
5 min read

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:

  1. Go to GitHub Settings > Developer settings > Personal access tokens.
  2. Click “Generate new token”. (I used fine-grained tokens, but you can use classic tokens if you are comfortable with that option)
  3. Select permissions (for private repository access).
  4. 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:

  1. Go to Settings > Secrets and variables > Actions.
  2. 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:

  1. Go to Settings > Secrets and variables > Actions.
  2. 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!

Continue Reading

Discover more insights and stories that you might be interested in.