Static Sites are Simple

Static Sites are simple. They're just files, and mostly text.

Here's a PowerShell one-liner to make a really simple static site:

"<h1>Hello World</h1>" > ./index.html

We make static sites with whatever language we want, and we can publish them about anywhere for free.

We can can stick them on https://github.com/.

We just create a repository and publish them with GitHub Pages.

We can use a free server, like https://neocities.org or https://wisp.place

We can find the last server on the planet supporting FTP and put our files up there, too.

It's all just copy/paste.

There are plenty of frameworks to use, but today let's see how simple it is to make a static site without building a whole framework.

We can use GitHub pages to host our site for free, and we can use PowerShell to deploy it.

We only really need two files to get going:

  • One GitHub Workflow
  • One or more scripts

The GitHub Workflow

Our workflow exists to call our scripts and publish our page.

We want to put this file in ./.github/workflows/deploy.yml

This is just a standard GitHub pages workflow, with a few bonus points:

  • It runs automatically about once a day, or on demand
  • It maps the published url to $env:page_url
  • It runs a script with the name of the workflow
  • It allows you to set a repository variable with a Google Analytics ID

It's pretty carefully documented, but we don't have to read it: we can just copy/paste it into the repo and call it a day.

To make this step a bit easier, I've pushed this workflow to:

New-Item -ItemType File -Path ./.github/workflows/deploy.yml -Force -Value (
    Invoke-RestMethod https://poshweb.org/workflows/deploy.yml
)

Here's the workflow you'll be deploying

# Simple workflow for deploying static content to GitHub Pages
name: deploy

on:
  # Runs on pushes 
  push:
  # and run on a schedule
  schedule: 
    # for those that don't speak cron, this runs:
    # * At the first minute (`1`)
    # * Every 21st hour (`1/21`)
    # * Every day of the year (`* * *`)
    - cron: '1 1/21 * * *'
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write

# Allow only one concurrent deployment, and cancel any in-progress deployments if a new one is triggered
concurrency:
  group: "pages"
  cancel-in-progress: true

jobs:
  # GitHub Pages use a single job, named deploy.
  deploy:
    # By using an environment, we avoid locking
    environment:
      name: github-pages
      # and we can control where it is deployed.
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
      # Check out our repository
      - name: Checkout
        uses: actions/checkout@main
        with:
          # Using fetch-depth: 0 to ensure we get the full history of the repository
          fetch-depth: 0
      # Setup GitHub Pages
      - name: Setup Pages
        uses: actions/configure-pages@main
      # To Build Pages in PowerShell, we just call a script      
      - name: Build Pages
        # set the shell to pwsh
        shell: pwsh
        # and then call any script we would like to build the page.
        # By default, this can use the same name as the workflow
        # (in this case, just deploy.ps1)
        run: . "./$env:GITHUB_WORKFLOW.ps1"
        # This approach makes it easier to logically organize workflow scripts.
        # We will also map the page_url to an environment variable, so our scripts can access it.
        env:
          page_url: ${{ steps.deployment.outputs.page_url }}
          analytics_id: ${{vars.ANALYTICSID}}
      - name: Upload artifact
        uses: actions/upload-pages-artifact@main
        with:
          # Upload the contents to the GitHub Pages artifact
          path: './'          
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@main

The site is built and deployed with .deploy.ps1

The Scripts

The scripts can be as complicated or simple as you want them to be.

So if we wanted to deploy hello world, all we need is:

"<h1>Hello World</h1>" > ./index.html

We can certainly style things up a bit. One easy way to do this is to create an array of content, and just output little bits of html.

@(
    # We can just throw html into quotes
    "<html>"
    "<head>"
    "<style>"
    # and do the same for css
    "body { max-width: 100vw; height: 100vh }"
    "h1 { font-size: 3rem; text-align: center }" 

    "</style>"
    "</head>"
    "<body>"
    "<h1>Hello World</h1>"
    "</body>"
    "</html>"
) > ./index.html

Keeping things simple, we can standardize look and feel across a website with a little bit of layout.

Just make a layout.ps1 file and pipe to it.

# We can collect all input quickly with $input
# (but we can only enumerate input once)
$allInput = @($input)


"<html>"
"<head>"
"<style>"
# Feel free to change the style
"body { max-width: 100vw; height: 100vh }"
"</style>"
"</head>"
"<body>"
# By joining input with newlines, we're treating any pipelined input the same
$allInput -join [Environment]::Newline 
"</body>"
"</html>"

Then we can do something very simply, like convert a README.md into html and pipe it to layout.

ConvertFrom-Markdown -Path ./README.md |
    Select -Expand Html |
    ./layout >
    ./index.html

Static Sites are simple.

This post is designed to get you started, and hopefully help you realize how PowerShell can generate static sites flexibly without a framework.

Please, try making some static sites with PowerShell. It's simple and fun.