I have been playing around with my domain as a basic placeholder website for about 7 years, but I have never got around to hosting anything but a plain html page. Sometimes I would put up a additional folder if I needed to host a demo, but for the most part it was unused. And this was because I had no experience in creating a simple website. My entire day job is building and writing web apps, with big ass backends and fancy frontends, but I can’t for the life of me create a simple website. Go figure. Plus the fact that I am a cheap skate and don’t want to pay for massive server that I’ll hardly use.

I wanted a presence online but one that I didn’t have to actively manage. I wanted something that I could setup and leave and not worry about it being compromised. Pretty quickly I discovered that creating pages and keeping them in sync style-wise was time-consuming because my IDE was Notepad++.

So when a colleague demonstrated the setup of his blog using Jekyll, I immediately liked it because it was a simple static site generator. Something with which I could create content locally using my dev tools and publish the output as basic html, no server side code required.

I started with just installing Jekyll 3.0 and getting a basic layout done. I chose Bootstrap because I was familiar with it. And I wanted to focus on the big wins, and show some progress in my efforts. I then started porting content from Yoda because I wanted to have some something to start with and while I was designing the look and feel, having content made that easier.

As I got into the swing of writing posts, updating design, tweaking things, I realised I would need some automation. I am always a fan of making my life easier, and removing friction/pain points. So I created a batch file to develop the site:

@echo off
setlocal

echo [%time:~0,2%:%time:~3,2%:%time:~6,2%] Starting Jekyll serve...

cd website\counihan.co.za\public_html

jekyll s --drafts

exit /B %ERRORLEVEL%

I started making more rapid progress once I could start the server automatically (one less thing to do when I open VSCode), and I updated the Blog and Tags pages using snippets I found online. I also struggled a bit with the code syntax highlighting and at first I couldn’t find a definitive preferred method of using Rouge vs Pygments.rb as the highligher.

Then I discoverd this post from Monica Granbois followed by this post, which gave me the css I needed as well as some guidance. So now I had syntax highlighting and line numbers! #Winning!

But what’s the point of a blog if no one reads it?! So I had to publish it online, and because I’m lazy I created another batch file for building to production.

@echo off
setlocal

echo [%time:~0,2%:%time:~3,2%:%time:~6,2%] Starting Jekyll build...

cd website\counihan.co.za\public_html

set JEKYLL_ENV=production

jekyll build

echo [%time:~0,2%:%time:~3,2%:%time:~6,2%] Completed Jekyll build.

exit /B %ERRORLEVEL%

At this point I added Google Analytics, just to get some metrics going, and worked out how to use environment variables for Jekyll

The upload to the FTP site was now becoming the friction point, so I created a PowerShell script to call the WinSCP dll.

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<#
Upload the files via sync to my site via ftp.
#>
param(
	[ValidateNotNullOrEmpty()]
	[string]$SourceFolder = "Output folder from the Jekyll build",
	[ValidateNotNullOrEmpty()]
	[string]$FTPHostname = "Ftp Host",
	[string]$FTPUsername = "Ftp User",
	[string]$FTPPassword = "Ftp Password", // I leave this blank and read the password from a text file later. Super duper secure I know...
	[string]$FTPDestinationFolder = "/public_html",
	[ValidateNotNullOrEmpty()]
	[string]$WinSCPNetDllPath = "Path to the dll"
)

# Get Start Time
$startDTM = (Get-Date)

# required to generate a proper exit code on errors.
trap 
{ 
  Write-Error $_.Exception | format-list -force
  exit 1 
}

Write-Host ("Set excution policy for the process...")

Set-ExecutionPolicy Bypass -Scope Process

Write-Host ("SourceFolder: {0}" -f $SourceFolder)
Write-Host ("FTPHostname: {0}" -f $FTPHostname)
Write-Host ("FTPUsername: {0}" -f $FTPUsername)
Write-Host ("FTPFolder: {0}" -f $FTPDestinationFolder)
Write-Host ("WinSCPNetDllPath: {0}" -f $WinSCPNetDllPath)

if ([String]::IsNullOrEmpty($FTPPassword)){
  $FTPPassword = Get-Content -Path "..\ftp.txt";
}

Write-Host ("Starting FTP Sync...")

try
{
    Write-Host ("Adding WinSCP dll...")

    # Load WinSCP .NET assembly
    Add-Type -Path $WinSCPNetDllPath -ErrorAction Stop

    # Setup session options
    $sessionOptions = New-Object WinSCP.SessionOptions -Property @{
        Protocol = [WinSCP.Protocol]::Ftp
        PortNumber = 21
        HostName = $FTPHostname
        UserName = $FTPUsername
        Password = $FTPPassword
    }

    # Session.FileTransferProgress event handler
    function FileTransferred
    {
        param($e)
    
        if ($e.Error -eq $Null)
        {
            Write-Host ("Upload of {0} succeeded" -f $e.FileName)
        }
        else
        {
            Write-Host ("Upload of {0} failed: {1}" -f $e.FileName, $e.Error)
        }
    
        if ($e.Chmod -ne $Null)
        {
            if ($e.Chmod.Error -eq $Null)
            {
                Write-Host ("Permisions of {0} set to {1}" -f $e.Chmod.FileName, $e.Chmod.FilePermissions)
            }
            else
            {
                Write-Host ("Setting permissions of {0} failed: {1}" -f $e.Chmod.FileName, $e.Chmod.Error)
            }
        }
        else
        {
            Write-Host ("Permissions of {0} kept with their defaults" -f $e.Destination)
        }
    
        if ($e.Touch -ne $Null)
        {
            if ($e.Touch.Error -eq $Null)
            {
                Write-Host ("Timestamp of {0} set to {1}" -f $e.Touch.FileName, $e.Touch.LastWriteTime)
            }
            else
            {
                Write-Host ("Setting timestamp of {0} failed: {1}" -f $e.Touch.FileName, $e.Touch.Error)
            }
        }
        else
        {
            # This should never happen during "local to remote" synchronization
            Write-Host ("Timestamp of {0} kept with its default (current time)" -f $e.Destination)
        }
    }

    $session = New-Object WinSCP.Session
    try 
    {
        # Will continuously report progress of transfer
        $session.add_FileTransferred( { FileTransferred($_) } )

        Write-Host ("Open WinSCP connection...")

        # Connect
        $session.Open($sessionOptions)

        Write-Host ("Syncing files...")

        # Synchronize files
        $synchronizationResult = $session.SynchronizeDirectories(
            [WinSCP.SynchronizationMode]::Remote, $SourceFolder, $FTPDestinationFolder, $True, $True)

        # Throw on any error
        $synchronizationResult.Check()
    }
    finally
    {
        # Disconnect, clean up
        $session.Dispose()
    }

    $global:LASTEXITCODE = 0;

    Write-Host ("FTP Sync completed")
}
catch [Exception]
{
    Write-Error ("Error: {0}" -f $_.Exception.Message)
    Write-Error $_.Exception | format-list -force
    $global:LASTEXITCODE = 1;
}

# Get End Time
$endDTM = (Get-Date)

# Echo Time elapsed
Write-host "Elapsed Time taken: $(($endDTM-$startDTM).totalseconds) seconds"

exit $global:LASTEXITCODE

This script is called from a batch file to save me more typing:

@ECHO OFF
PowerShell.exe -NoProfile -ExecutionPolicy Bypass -NonInteractive -WindowStyle Hidden -File "syncFilesToWebsite.ps1"

And BOOM! my site is live and updated in minutes. The next big challenge will be keep on publishing posts on a regular basis, so please come back often.