FCards - On the Web

27. Sharing your game with the world

We now have a finished product and we are ready to share it with the world!

Publishing to a distributable

The first step of showing off our new game is to package it up into a form that is compatible with a web server. This is fairly easily done using dotnet’s publish command.

dotnet publish Website/Website.fsproj -o ./dist -c Release --nologo

This creates a working website in the “./dist” folder with all the extra debug stuff stripped out for release.

If you look at what is produced, you get a web config file, the program, and all the framework libraries to make F# work.

Technically, this is all you need - you could upload this to a webserver using plain old FTP and point to the index.html file and it would just work. But this is and F# and dotnet series, so let’s see what it takes to get things running on Azure.

Creating infrastructure using Farmer

Farmer is a community build project for generating the definition file (called a ARM template) that is used to manage Azure resources. A resource might be a storage account, or a website, or maybe even a database, that is hosted in Azure.

To use Farmer, we describe the Azure resources and how they link together using an F# script file, just like the first versions of our game. The Farmer library is referenced at the top of the script directly from NuGet.org and used to create a description of the resources we desire and then save that as a ARM template file. We can even execute the creation of the resources in Azure directly from within the script!

Note that this is just a normal F# script, and so can be as simple or complex as we want.

Code so far

deploy.fsx

#r "nuget: Farmer"  // Get the Farmer libraries directly from NuGet

open System
open Farmer
open Farmer.Builders

// We'll pass the path to our published game in as an argument
let pathToGame = Environment.GetCommandLineArgs() |> Array.last
printfn "THE PATH TO THE GAME IS: %s" pathToGame

let storage = storageAccount {
  name "solitairestorage"
  sku Storage.Sku.Standard_LRS
  use_static_website pathToGame "index.html"
  static_website_error_page "error.html"
}

let deployment = arm {
  location Location.AustraliaSoutheast
  add_resource storage
}

// Save as a ARM template file, just to check we got it right (optional)
deployment 
|> Writer.quickWrite "output"

// Deploy into resource group directly to Azure
deployment
|> Deploy.execute "solitaire-rg" Deploy.NoParameters
|> printfn "%A"  // (print out any results - not expecting any)

deploy.sh


dotnet publish Website/Website.fsproj -o ./dist -c Release --nologo

dotnet fsi ./deploy.fsx ./dist/wwwroot

To run this, you will need to have signed up for an Azure account, and will need to install the Azure CLI to login from the terminal. You may need to execute az login first.

Once this has run, you can the url of the new website by:

  1. login to the Azure Portal,
  2. Navigate to the new resource group solitaire-rg
  3. then to the storage account solitairestorage
  4. then about halfway down in the left panel find Data management > Static website
  5. On this page is the Primary Endpoint, which is the URL of the page.
    It should look something like https://solitairestorage.1234.web.core.windows.net/

Open the URL in a browser and there you go - your own game on the internet for anyone to enjoy!

Alternative: using GitHub pages to build and host

This is an alternative to hosting the game on Azure. As this game is after all a static website, with no backend program, we COULD host it anywhere. So let’s try getting GitHub Pages to build and host our game without have a separate host at all!

Github Pages can use a build action to compile the F# code and then use the index.html file as the “Page”.

To make this work we need a couple of things:

To do this we need to give it a “workflow” yaml file and store it in the special location .github/workflows/main.yml.

If all goes well, you should see something going on in the “Workflows” tab on the github portal every time you push a change to the repo, and be able to see the game at

https://<your organisation/account name>.github.io/<your repo name>

There is an example of this (using a custom domain 😊) at https://pete-the-programmer.com/Solitaire-Demo/

Code so far

_github/workflows/main.yml

name: Deploy to GitHub Pages

# Run workflow on every push to the main branch
on:
  push:
    branches: [ main ]

jobs:
  deploy-to-github-pages:
    # use ubuntu-latest image to run steps on
    runs-on: ubuntu-latest
    steps:
    # uses GitHub's checkout action to checkout code form the main branch
    - uses: actions/checkout@v2
    
    # sets up .NET 6.0
    - name: Setup .NET
      uses: actions/[email protected]
      with:
        dotnet-version: '6.0.x'

    # publishes Blazor project to the release-folder
    - name: Publish .NET Project
      run: dotnet publish Website/Website.fsproj -c Release -o release --nologo
    
    # copy index.html to 404.html to serve the same file when a file is not found
    - name: copy index.html to 404.html
      run: cp release/wwwroot/index.html release/wwwroot/404.html

    # add .nojekyll file to tell GitHub pages to not treat this as a Jekyll project. (Allow files and folders starting with an underscore)
    - name: Add .nojekyll file
      run: touch release/wwwroot/.nojekyll
  
    - name: Commit wwwroot to GitHub Pages
      uses: JamesIves/[email protected]
      with:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        BRANCH: gh-pages
        FOLDER: release/wwwroot

Note: If you’re using GitLab, or Bitbucket, or something similar, then these hosts will usually be able to do something like this as well