After publishing this site using GitHub Pages (and Jekyll), I immediately started looking for ways to open all the external URLs in a separate browser tab. I came across the Jekyll Target Blank plugin very quickly, but then I realized I can’t use it with the standard GitHub Pages workflow – because it only supports a certain set of plugins.

I did not settle for this situation: there must be a solution to this problem! And it turned out that there is one: I only have to create my own GitHub Actions workflow that builds the artifact which is then deployed to the GitHub Pages web server.1

Controlling the Jekyll build process

I basically followed Samplasion’s post. I don’t want to copy it here, please read it carefully. There is only one thing to take care of: Read and write permissions must be granted to workflows in the repository for all scopes.

​You have to navigate to the repository Settings, select Actions on the right-hand side, and you can change this configuration on the bottom of the page, at the Workflow permissions section.

Workflow permissions settings on GitHub Workflow permissions settings on GitHub at https://github.com/[name]/[repository]/settings/actions

The joshlarsen/jekyll4-deploy-gh-pages plugin I’m using to build and publish the artifacts is designed to push the build artifact into a gh-pages branch, and this cannot be modified2. So before you proceed, you have to create the gh-pages branch.

This was the workflow YAML I used:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
name: Github Pages Custom Build
on:
  push:
    branches:
      - main
jobs:
  github-pages:
    runs-on: ubuntu-latest
    steps:
      -
        name: GitHub Actions Checkout
        uses: actions/checkout@v2
      -
        name: Cache
        uses: actions/cache@v2
        with:
          path: vendor/bundle
          key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
          restore-keys: |
            ${{ runner.os }}-gems-
      -
        name: Jekyll Build & Deploy
        uses: joshlarsen/jekyll4-deploy-gh-pages@v1.8
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_REPOSITORY: ${{ secrets.GITHUB_REPOSITORY }}
          GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }}

After I saw that this workflow finished successfully, I changed my GitHub Pages configuration: I set the source branch to gh-pages, pushed a test commit into the main branch – and it was published in less than 2 minutes!

Being able to push from a private repository into a public one

Maybe because of the success, I wanted to solve another new task. I’m quite shy 😉, I don’t want to publish all the changes I make or my public commit history. Can’t we publish the build from a private repository somehow (for free)?

Yes, we can!

  1. I created an orphan branch in my public GitHub Pages repository temporarily, and named it test. My goal was to push into this branch.
  2. I created a new private repository (let’s say it is huzooka/github-site. This is the repository which will contain all the source files.
  3. In the private repository, I created a branch named main. This contained a single test.txt with some random text.
  4. I granted read and write permissions to workflows in the private repo.
  5. To be able to push to the public repository, I had to create a PAT in the public GitHub Pages repository, and then add it as a secret to the private repository. This is essential: without a token you won’t be able to push the build to the public repo. Please follow Kumar Rohit’s post (I did that too).
  6. I started to write a very simple workflow in the private repo, and each commit I have been sitting on needles and pins waiting for the text file to appear in the public repository. This worked:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    name: Build and release
    on:
      push:
        branches:
          - main
    env:
      BUILD_DIRECTORY: 'destination'
      TARGET_REPOSITORY: 'huzooka/huzooka.github.io'
      TARGET_BRANCH: test
    jobs:
      build:
        runs-on: ubuntu-latest
        steps:
          -
            name: GitHub Actions Checkout
            uses: actions/checkout@v2
            with:
              path: source
          -
            name: Do some dummy action # @todo: Replace with a Jekyll build.
            run: |
              mkdir -p "${GITHUB_WORKSPACE}/${BUILD_DIR}"
              cp ${GITHUB_WORKSPACE}/source/*.* ${GITHUB_WORKSPACE}/${BUILD_DIR}/
              md5sum "$GITHUB_WORKSPACE/${BUILD_DIR}/test.txt" > "$GITHUB_WORKSPACE/${BUILD_DIR}/test.md5sum"
          -
            name: Release to Public Repository
            run: |
              cd "$GITHUB_WORKSPACE/_site"
              export BUILD_BRANCH=`git rev-parse --abbrev-ref HEAD`
              git push --force "https://${GITHUB_ACTOR}:${{ secrets.GH_PAGES_REPO_TOKEN }}@github.com/${TARGET_REPOSITORY}.git" ${BUILD_BRANCH}:${TARGET_BRANCH}
    

Combining the above

Finally, I combined the steps above together. I’m pretty sure there are more configurable Jekyll build plugins, but I kept using joshlarsen/jekyll4-deploy-gh-pages. It has another limitation though: because of some reasons3, I wasn’t able to make it push into another repository.

I can live with that: I let it push into the private repository’s gh-pages branch 🤷‍♂️. But after that step finishes, my next workflow task pushes the same artifact into my public GitHub Pages repo too.

This is my private GitHub Actions workflow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
name: Build and release
on:
  push:
    branches:
      - main
env:
  TARGET_REPOSITORY: 'huzooka/huzooka.github.io'
  TARGET_BRANCH: gh-pages
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      -
        name: GitHub Actions Checkout
        uses: actions/checkout@v2
      -
        name: Cache
        uses: actions/cache@v2
        with:
          path: vendor/bundle
          key: ${{ runner.os }}-gems-${{hashFiles('**/Gemfile.lock') }}
          restore-keys: |
            ${{ runner.os }}-gems-
      - 
        name: Jekyll Build & Dummy Deploy
        uses: joshlarsen/jekyll4-deploy-gh-pages@v1.8
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_REPOSITORY: ${{ secrets.GITHUB_REPOSITORY }}
          GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }}
      -
        name: Release to Public Repository
        run: |
          cd "$GITHUB_WORKSPACE/_site"
          export BUILD_BRANCH=`git rev-parse --abbrev-ref HEAD`
          git push --force "https://${GITHUB_ACTOR}:${{ secrets.GH_PAGES_REPO_TOKEN }}@github.com/${TARGET_REPOSITORY}.git" ${BUILD_BRANCH}:${TARGET_BRANCH}

Sources:


Footnotes:

  1. This is performed by the pages build and deployment action. This action is the essence of GitHub Pages, but it is not completely under our control, we cannot modify it or find it in our codebase. As soon as we enable GitHub Pages on a repository, it suddenly appears, and if we turn it off, it disappears 🙂. 

  2. Yes, I know I can fork any repository anytime. But why would I want to maintain yet another package? 

  3. It seems that we can configure it to push to another repo, but it doesn’t work – and I really don’t have an idea why. The action finished successfully, but nothing was pushed to the public repository