Deploying a VuePress static site with Azure Static Web Apps, and GitHub Actions
tl;dr / We're going to take a semi-beginner approach to static site development, and deployment using Git, VuePress, GitHub Actions, and Visual Studio Code. This is long because I try to make no assumptions about what you know, and link to resources on all of these things with the goal of providing education. I walk through setting up from scratch a repo, a new project with a framework dependency, and then building a resource within Azure and connecting it to GitHub. We finally walk through editing the github actions pipeline to support VuePress which is not covered in the sample documentation at docs.microsoft.com.
update: added some clarity that I found around the scripts block in package.json at the end of the article to explain adding 'build:azure' as a script that the GitHub action will look for.
A bit of background
I've been working in the technology industry for over 20 years and got started with "static" websites when I was a kid. I would build basic HTML and CSS websites using notepad. Back then we had FrontPage to help as a WYSIWYG editor, but it was expensive, and annoying with how it messed with the underlying code. Dreamweaver also existed, but it was outside my 17 y/o budget. I would get my HTML, CSS, and assets all together then I'd use FTP to upload that to a location where a web service would serve them up. Or I could copy and paste my files into GeoCities and have them host it. Or I could run Personal Web Server and have my website available at the IP address of my dial-up connection, again, no budget, no domain registration at the time, and really no audience ????. Most people were still getting things with NNTP, FTP, and GOPHER back then. The web was still pretty new. I'm going to write this article from a slightly more knowledgeable than NOOB point of view, but still new to things like Markdown, CI/CD pipelines, and Azure.
The web has evolved tremendously since those late 90s, and good thing! So many things have made web site development richer, and more powerful, but also very, very complex. Of course you can still write generic HTML, and CSS, and upload your files to a web server to get served up to end-users, but where's the fun in that? Today's "hello, world" website should consist of modern development practices, which means using a cloud service, with a deployment pipeline, and a relevant underlying framework.
At Build 2020 Microsoft announced a brand new service within Azure that allows you to host a static web page absolutely free! It's called Azure Static Web App. While it's true that there was the ability to host a static website from a storage account, released last year, if your static site consisted of a huge amount of stored files, you would have to pay for that storage in the account. With this service, your files are hosted in a GitHub repo, and then a GitHub Actions workflow "builds" your site and deploys it to the static web app service. All you need to do is pick a framework.
The image above was lifted from the Static Web App overview documentation. It highlights quite nicely the underlying architecture of what happens with this service. It's important to call out that there's an API configuration that you can setup, but that's beyond the scope of this write-up.
Diving right in
If you're just getting started, you'll want to have a few things on your workstation before you start, they are listed below. I'm not going to go into how to configure these things as there are countless resources on the internet. My workstation is a Surface Laptop 2, with Windows 10 (2004 release, which will be available on 28 MAY or is available now on Windows Insider Slow ring) running a WSL2 instance of Ubuntu Focal Foosa (20.04). I'm running git, and vscode remote in this example, so everything is happening in the WSL2 instance, and being presented in Windows. For a framework we're going to use VuePress. I'm not saying this is the best framework, there are a lot of religious/technical discussions on the frameworks today, this is just what I chose to explore and learn about, and then share with you.
- Azure Subscription, https://azure.microsoft.com/en-us/free/
- GitHub account, https://github.com/join
- Software installed Git, and Visual Studio Code, nodejs, and yarn.
Setting up our repo
The first thing you want to do is setup your local code environment that will be pushed to GitHub. You can certainly build a repo within github and then pull it down. In this instance we're going to do the slightly more complex setup on your local machine, and setting the origin to github and then pushing the code we create.
- Navigate to your chosen file location on your workstation. I happen to use C:\Code, or ~/Code in WSL for all my projects.
- Create a new folder, whatever you like, make it memorable.
- Navigate to that folder and type the command `git init` this will initialize git database and start tracking everything in your folder.
- Next, open your favorite browser (mine is Microsoft Edge insider DEV channel )
- Navigate to www.github.com and sign in.
- Click on New to create a new repo (github.com/new)
- Setup a repo name, this can be the same name as the folder you created on your computer, for technical clarity, and readability sake this is best, but it can be whatever you want.
- Add a description so you know what this repo is about
- Make it public.
- DO NOT initialize it with a README
- Click Create Repository
GitHub presents this page after you've created your repo to get you started. This is super helpful I think in providing some skills with git. I've highlighted above the next step that we have to connect the repo we initialized on our workstation to this repo on github.
On your workstation, your CLI should still be open from the steps above. Ensure that you're still in the repo we created and run the commands highlighted. You can click the little clipboard icon to the right and copy the commands if you want.
If you've run all the commands that GitHub gave you, you'll get an error when you run `git push -u origin master`. This is because you haven't committed anything to the repo on your workstation, so there's nothing to push to the master branch. We'll add some files next to get us started and push those to master.
Setting up the site
At this point you should have all the software setup. If you haven't you should go back and ensure that you've installed node and yarn.
I'm going to copy and paste the startup from the vuepress documentation for an existing project to get us started. You should definitely continue reading through that once we're done here to learn more.
# install as a local dependency yarn add -D vuepress # OR npm install -D vuepress # create a docs directory mkdir docs # create a markdown file echo '# Hello VuePress and Static Web Apps!' > docs/README.md
Now that the project is set up with an initial file we're going to launch VS code remote and start working in our development environment.
At the CLI you can type `code . ` which tells VS Code to launch in the folder that you're working in. If you're using WSL2 you'll see ubuntu (assuming you're using ubuntu) downloading the VS code server, and then launching the editor on Windows. You can see that you're working in the WSL environment if you look at the lower left-hand side of the VS Code window.
When you first bring up VS Code you're going to get an error message about too many active changes, and would you like to add the `node_modules` to .gitignore say yes to this, and we'll walk through setting up the remainder of the .gitignore. .gitignore basically tells git to not track particular files in your repo. You want to exclude everything from the repo that will be built by something else, either the pipeline, or build process from your framework. The only thing you want git to track is the code that you write, or in this case the settings files, and the markdown documents for your static website.
Your environment should look like the screen below.
There's a couple of things going on here (theme is GitHub Sharp). In the SIDEBAR to the left you'll see that you have several icons. The first is that the git control icon in the side bar shows you that you have uncommitted files, those files are highlighted in GREEN. The second is that the .gitignore file is open as your first editor window.
Let's update that with what we know to work with vuepress. You can find that by using your favorite search engine to get the vuepress gitignore settings
# Dist files lib/ # Node modules node_modules/ # MacOS Desktop Services Store .DS_Store # Log files *.log # Typescript build info *.tsbuildinfo
Add the above code to your .gitignore and save it.
Next let's edit our package.json file. This file tells yarn what to do with our site, what dependencies to install, and what commands to run when we perform build actions. Right now, it is pretty blank, it should look like this below.
Add the following code
{ "scripts": { "docs:dev": "vuepress dev docs", "docs:build": "vuepress build docs" } }
This sets up the project to either compile the files and launch a web server to test, and or build the entire site for deployment. You can read more in the vuepress documentation but for now this is enough. Save this file.
Let's test it out to see that it works. Hit CTRL + ` on your keyboard to launch the terminal window within VS Code. then type yarn docs:build to build your site.
Hopefully your site built correctly. You'll know this because you see a .vuepress/dist folder underneath the docs folder in the file view. You'll notice that there's some .html files and an asset folder, and they're all marked green. This means that git is tracking them, so we'll want to update our .gitignore to exclude the 'build' folder from our project. Remember we only want to track code that we edit, not things that are built out.
Add the following line to your gitignore under the # DIST FILES heading
docs/.vuepress/dist
This will exclude our build files from tracking.
Next let's setup a config.js file in the .vuepress folder. The config.js file will control settings for our vuepress site. You can read more about the configuration here when we're done. For now right click on the word .vuepress within the file explorer of VSCode, and click NEW FILE. Type in the name config.js and hit enter.
Add the following code to your config.js
module.exports = { title: 'Hello VuePress', description: 'Getting started with static sites of the future' }
Save it.
With the config.js set, lets run our site and browse to it to see what it looks like. Gotta warn you, it's pretty simple, but don't be discouraged this is just your starting point.
In the terminal window type yarn docs:dev , this will compile the markdown files, and will launch your site at https://localhost:8080, bring this URL up in the browser once the terminal window says it's ready and waiting.
Your browser should look like this
Pretty simple, but what you have here now is a documentation website with a home page, compiled off of a single echo line written to a markdown file. The basic foundation for your static site of the future.
Hit CTRL-C in the terminal window, this will close the web server, and bring back your CLI prompt. We're now ready to commit these files and push them to github. You can do this from the GUI of VS Code and feel free to work on that if you want. I'm going to show you the CLI way of doing your commit below.
In the terminal window type `git add *` this takes all the green labeled files in VS Code and 'stages' them for commit to the repo. As you work with git and software development you'll find different ways to work, sometimes you'll stage a single file, sometimes grouping files together depending on how you want to track history of changes for your code. For our purposes we just want to create an initial commit of everything we've worked on to this point.
Because the '.' in front of the .gitignore hides this file, we need to manually include it by typing `git add .gitignore` we want to include this with our source code.
Finally type `git commit -m 'Initial Commit'` the git commit command adds the files to the source control and the -m adds the message for what was committed.
You should see some form of output that states the files were added like the screen above shows.
If this is the first time you've committed to github from within VS Code, it will want to authenticate to github. Even though your repo is public, you're the only one who can edit it, and therefore you have to authenticate. VS Code should launch another page in your browser where you'll authenticate to github, it'll create an OAuth token for VS Code and then redirect you back to the VS Code window. DO not do this. The OAuth token it sets up does not allow you to edit the workflow which we'll need later on.
You'll want to setup a github token to do this. Follow the instructions here on how to do this. Copy the authentication token to a secure place you can get to it later. You will want to setup the token with the following privileges
repo and workflow
You can certainly edit your token later on but this makes it easier for the rest of this document.
Once you've created your token now all we do is type `git push --set-upstream origin master` VS Code with ask you to authenticate. Enter your github userID / email address, and the PAT Code you copied earlier. VS Code will save this and authenticate, and then push the files to our github repository on the master branch.
If you want to know more about how to use git, and you will want to, checkout the free book at the git website PRO GIT.
If all went well you should see code within your repo.
FINALLY! Setting up the Azure Static Web App Resource
Head on over to the Azure Portal , login and get to your home screen.
Click on Create A Resource. If you're at the start page like shown above, or click the GREEN + sign on the sidebar to create a new resource.
At the resource screen type in 'Static Web App (Preview)'. The auto complete might find it before you finish, if so just click on it. You'll see the screen below
Click CREATE
In the first screen of the wizard, select your subscription. Then click CREATE NEW under Resource Group. Type in the name of the repo in github, and click OK. This is purely to keep things together nicely in your head, it has no bearing on the actual repo and connecting it. If you already have a resource group, select that resource group from the drop down.
Type in the name of the github repo in the Static Web App Details NAME field, or choose your own. Select a region, whatever region you've always wanted to host a website in. I've chosen West US2.
Next click on SIGN IN WITH GITHUB. If you've already authenticated to github, the azure portal will flash up a window while it grabs a token to connect to github. If not, you'll be asked to authenticate.
In the source control details, pick the org (in most cases there's only one), choose the repo we've been working on, and the MASTER branch. Click on NEXT:BUILD>
Leave the APP LOCATION field as it is, we want our website to start at the root which is defined by this '/'. Clear out the api location field, we don't have an api folder.
In the app artifact location, you want to enter the output directory we know our vuepress site will build in when we run yarn docs:build in this case our location is 'docs/.vuepress/dist' . Click on NEXT:TAGS>
Fill in some tag values. I wouldn't be a good cloud practicioner if I didn't admonish you to do so. Tags help with everything in cloud deployments, and getting the practice ingrained into you will endear you to procurement and finance folks everywhere.
Click NEXT: REVIEW + CREATE >
Azure will validate our deployment settings, everything should look correct on this screen. If something is wrong, click the previous button, or click on the headers to navigate to that portion of the Wizard that needs correcting and correct it. Otherwise click CREATE
Once the deployment is complete you'll see a success screen. Click on GO TO RESOURCE. I can't imagine a scenario where this doesn't complete but if it doesn't you'll see diagnostic info here that you can use to correct and re deploy the resource.
A successfully deployed resource will look like the one above. The URL for your website is listed under the URL location above. Click on it to navigate to your website. It's important to note that it's autogenerated. Don't get hung up on it, because you can change the custom domain if you'd like from the custom domains section on the left.
WAIT! Why do I see this and not my site??? Good question! We uploaded our site, but the default github actions doesn't do anything to actually build our site. This is where we get a chance to work with our pipeline and edit the actions that tell github to build the site, and deploy it to Azure.
Go back to the resource in the Azure Portal and look for the Workflow file link
Click on this file, it'll bring you to github, where we can see what is going on.
This is your standard GitHub actions .yml file. It defines the actions and steps taken to deploy your site to the Azure Static Web App resource. You can read more about the GitHub actions here.
What we want to do is inject a step that tells the GitHub actions agent, which in this case is an ubuntu-latest version container (or vm, but more likely a container) to pull our repo, run yarn docs:build and the copy the docs/.vuepress/dist folder to the root of our Azure Static Web App. Thankfully for us, 99.999% of this logic is already in the default github action file. It uses a tool called oryx from Microsoft, which what I gather is something they use internally to deploy resources into the Azure fabric, they're making it available to us.
Head back to VS Code and in the terminal window type `git pull` because the Azure Static Web App service added a github action to our code, we need to synchronize it with the copy of code we keep on our machine. It also helps with editing.
Navigate to the '.github/workflows' folder and open the .yml file. We want to add a command to the BUILD and DEPLOY job. If you read the documentation, you'll see this parameter `app_build_command` that we can utilize to achieve our task. You'll have to trust me on this, that the container has node, and yarn on it. I was able to verify this in my other test case because I added a pre-step that actually ran local commands to see which version of node was installed on this 'container'. See if you can figure out how to do this yourself.
With the .yml file open add the line `app_build_command: yarn docs:build` to the file, as shown below
Save the file and return to the terminal to commit the file and push to github. This is where you will run into problems if you did not authenticate with the PAT created earlier. That PAT has workflow rights, and will allow the push to happen successfully.
Once it is uploaded go back to github and locate your repo, click on ACTIONS. You'll see something like the screen below depending on how long it takes to generate the build. The pipeline run name is based off the commit message we added when we pushed to master, our single update of the yml file.
You can see it took only 1m 35s for the pipeline to run. It has a green check mark which means all steps completed successfully, even our yarn docs:build command.
If you click on the title 'Update Workflow' in this case you'll drill down into the action. Click on the BUILD AND DEPLOY job to see the output of the agent that ran this pipeline for us.
You can see the same output you would see on your workstation when you run yarn docs:build which is awesome because then you know this agent is working correctly.
Finally we see that our website uploaded correctly and the deployment completes. If you still have your static site up in your browser refresh the screen it should look like below.
There you have it! Of course there are a multitude of ways to get a static site on the internet. This is just one of them, and I hope you enjoyed working through using VSCode, with VuePress, WSL2, and GitHub Actions! If you're just getting started with web development, you've now handled some tools that will come in handy in the future. If you've done this before it might seem simple but hopefully you've learned more about one piece of technology, either through this article or the linked articles, that you didn't know before.
Thanks for sticking it out with me, I gladly welcome any feedback you might have!
--john
UPDATE: While working through this for another demonstration, something else clicked that I had never seen explained well before. When you look at the code in package.json under `scripts` we added `docs:build` and `docs:dev` and then a string of code after that. You can add as many key - value pairs here as you want, and specify different command strings to control deployment or build. When you upload this package as is to the repo, it fails in the build and deploy job in the github action. The output that it gives is that it couldn't find a `build` or `build:azure` command in the scripts section of the package.json file.
You can skip the whole section on adding `app_build_command` to the yml file, if you add the following line to your scripts section.
"build:azure" : "vuepress build docs"
This will handle the build and deployment in the during the very first push to master.
Senior Software Engineer at Cubic Telecom
3 年Awesome. So far I found there is only one problem setting export destination like in .vuepress/config.js like module.exports = ctx => ({ ??dest: 'public', and setting app location in action app_location: "/public" By some reasons it does not work. It works only for root folder app_location: "/" and dest not set.
Cloud Solution Architect
4 年So cool. Thanks for sharing John
Lead Engineer at LegalZoom
4 年Do we something similar as cloudfront like aws has ?
Senior Developer Fullstack Developer | Angular, .NET, NodeJs
4 年Nice article! Looks easy to follow and the illustrations make it nice and clear