Many developers maintain separate repositories for different types of content. For instance, you might have your main portfolio website in one repository and detailed technical notes (like C++ notes) in another. But what if you want to publish those notes as part of your main website, automatically updating the site whenever the notes change?
This post details a robust and scalable method to achieve this using Jekyll, Netlify, GitHub Actions, and a custom build script. We’ll use the example of integrating a cpp-notes
repository into a main portfolio site.
The Challenge
We want to:
- Keep the
cpp-notes
repository completely separate from the main portfolio site repository. - Automatically trigger a rebuild and deployment of the portfolio site on Netlify whenever changes are pushed to the
cpp-notes
repository. - Include the content from
cpp-notes
within the Jekyll build process of the main site. - (Optional but common) Handle cases where the external content (e.g., Markdown files) might lack the YAML Front Matter Jekyll needs for processing within collections.
The Solution: Build Script & Automation
We’ll orchestrate this using several components:
- Netlify Build Hook: A unique URL provided by Netlify that triggers a new build for your main site when requested.
- GitHub Secret: Securely stores the Netlify build hook URL within the external notes repository.
- GitHub Action: A workflow within the external notes repository that automatically sends a request to the Netlify build hook URL upon a push event.
- Custom Build Script (
netlify-build.sh
): A shell script within your main portfolio repository that Netlify will execute. This script handles cloning the external repository, performs any necessary pre-processing (like adding front matter), and then runs the standard Jekyll build. - Netlify Configuration (
netlify.toml
): Tells Netlify to execute our custom build script instead of the default build command. - Jekyll Configuration (
_config.yml
): Defines the directory where the external content is cloned as a Jekyll collection so it gets processed correctly.
Step-by-Step Implementation
Here’s how to set it up:
1. Create Netlify Build Hook (Main Site)
- Log in to Netlify and navigate to your main portfolio site’s settings.
- Go to Site settings > Build & deploy > Continuous deployment > Build hooks.
- Click “Add build hook”.
- Give it a descriptive name (e.g.,
trigger-from-cpp-notes
). - Select the branch Netlify should build when this hook is triggered (usually your production branch, like
main
orjekyll-integration
). - Click “Save”.
- Important: Copy the generated URL. You’ll need it for the next step.
2. Create GitHub Secret (External Repo)
- Navigate to your external notes repository on GitHub (e.g.,
cpp-notes
). - Go to Settings > Secrets and variables > Actions.
- Click “New repository secret”.
- Set the Name to
NETLIFY_BUILD_HOOK_URL
. - Paste the build hook URL copied from Netlify into the Value field.
- Click “Add secret”.
3. Create GitHub Action Workflow (External Repo)
- In your external notes repository (
cpp-notes
), create the following directory structure and file if it doesn’t exist:.github/workflows/trigger_netlify_build.yml
. -
Add the following content to
trigger_netlify_build.yml
:name: Trigger Netlify Build on: push: branches: - main # Or whichever branch you push final notes to jobs: build: runs-on: ubuntu-latest steps: - name: Trigger Netlify Build Hook run: curl -X POST -d '{}' $
- This workflow triggers on pushes to the specified branch (
main
in this case) and sends an empty POST request to the build hook URL stored in the secret, kicking off a Netlify build for your main site. - Commit and push this file to your external notes repository.
4. Create the Build Script (Main Site Repo)
- In the root directory of your main portfolio repository (
vaibhavdeokar-portfolio
), create a file namednetlify-build.sh
. -
Add the following content:
#!/bin/bash # Exit script if any command fails set -e echo "Cloning external repositories..." # --- C++ Notes --- echo "Cloning cpp-notes..." # Replace with your actual repo URL if different git clone https://github.com/dev-ai-kar/cpp-notes.git _cpp_notes # --- Add future repos below --- # echo "Cloning repo2..." # git clone https://github.com/user/repo2.git _repo2_notes echo "Processing cloned repositories..." # --- Add front matter for cpp-notes (if needed) --- echo "Adding front matter to cpp-notes..." # This 'find' command adds empty front matter to all .md files find _cpp_notes -name '*.md' -exec sed -i '1i---\ ---' {} + # --- Add processing for future repos below (if needed) --- # echo "Adding front matter to repo2..." # find _repo2_notes -name '*.md' -exec sed -i '1i---\ # ---' {} + echo "Running Jekyll build..." # Use 'bundle exec' if you manage gems with Bundler bundle exec jekyll build echo "Build script finished."
- Explanation:
set -e
: Ensures the script stops if any command fails.git clone ...
: Clones the external repository into a specified directory (here,_cpp_notes
).find ... -exec sed ...
: This line is crucial if your external markdown files lack front matter. It finds all.md
files in the cloned directory and usessed
to insert an empty front matter block (---\n---
) at the beginning of each file. If your external files already have front matter, you can remove thisfind
command.bundle exec jekyll build
: Runs the standard Jekyll build process.
5. Set Execute Permissions (Main Site Repo)
- Before committing
netlify-build.sh
, you need to tell Git it should be executable. In your local terminal (Git Bash, Linux, macOS):# Stage the file first if you haven't already # git add netlify-build.sh git update-index --chmod=+x netlify-build.sh
- This command updates Git’s index to record the execute permission for the script.
6. Update netlify.toml
(Main Site Repo)
- Edit the
netlify.toml
file in your main repository. -
Modify the
[build]
section to execute your new script:[build] # Execute the build script instead of the direct command command = "./netlify-build.sh" publish = "_site/" # Jekyll's default output directory # Keep other settings like [build.environment] if needed [build.environment] RUBY_VERSION = "3.4.2"
7. Update _config.yml
(Main Site Repo)
- Edit the
_config.yml
file in your main repository. -
Define the directory you cloned into (
_cpp_notes
) as a Jekyll collection:collections: posts: # Your existing posts collection output: true permalink: /blog/:year/:month/:day/:title.html cpp_notes: # The new collection output: true # Generate pages for each note permalink: /notes/cpp/:path.html # Define the URL structure
- Adjust the
permalink
as desired.:path
ensures the directory structure from_cpp_notes
is reflected in the URL, and Jekyll handles the.md
to.html
conversion.
8. Commit and Push (Main Site Repo)
- Commit the new
netlify-build.sh
script and the changes tonetlify.toml
and_config.yml
. Use a descriptive commit message. - Push these changes to the branch Netlify deploys (e.g.,
jekyll-integration
ormain
).
Making the Content Accessible
Now that the external notes are being included in the build, you need to provide ways for users to find them:
- Create an Index Page: Add a new page (e.g.,
notes/cpp.md
or similar) in your main repository. Use Liquid to loop through the collection and list the notes: (Note:note.data.title
assumes you might add atitle:
field in the front matter later; otherwise,note.name
gives the filename.) - Add Navigation: Edit your site’s header include (
_includes/header.html
or similar) to add a link to your new index page.
Conclusion
This setup provides a powerful, automated, and scalable way to manage content across multiple repositories while presenting it seamlessly on a single Jekyll site built with Netlify. By leveraging build hooks, GitHub Actions, and a custom build script, you maintain separation of concerns while ensuring your site always reflects the latest updates from all your content sources. Remember to adapt repository URLs, branch names, and directory structures to match your specific project needs.