Setting Up This Website - Static Web Apps in Azure

Posted on Dec 14, 2021

This website, right now, is set up as a static web application in Azure. Why, you ask? Because it can be. Because it’s clever. Because it can be useful. Because it’s cheap. Because it’s easy. Mainly because it’s cheap and easy. I didn’t come about this initially and, actually, when I first started looking into this a few years ago the concept was still fairly new and Azure had most of the features I will end up using for this site in preview, if at all.

If you want to skip the preamble and get down to business go ahead and skip to Deploying a Hugo Site on Azure

Table Of Contents

The Roads Not Traveled

I’ve deployed some other sites through various mechanisms and I would like to point out, prior to getting started, why I don’t think that they completely make sense for my purposes but why they might make sense for others. The Internet, technology in general, and people in particular, are varied things. For a personal site thinking about these choices in any great depth may not have any appreciable impact, and in fact, there may be cases where they’re more appropriate but it’s worth having a brief discussion.

WSYWIG Content Management Systems

If I’m showing my age, I’m sorry, but WSYWIG (what you see is what you get) CMS products were where the web was moving towards when I got started in technology. It’s interesting that we’re seeing the rebound these days and there are a few reasons why. CMS systems, especially ones designed to intake human-readble designed content, generally aren’t great at automation. They are very popular, however, and once set up they allow people to generate their content quickly and easily. Marketplaces with themes ready-made make it simple to get something that looks nice enough and there is an entire industry based on developing templates on a contract basis so outsourcing is available. These systems also do a great job of managing all of the “back end” stuff. Some are very good at having multiple users contribute. You can do backups and restores with a few clicks (generally). You don’t need to do much, if at all, to get the cross links and all that functional. It’s good stuff. It does a lot but it does it at a cost.

Part of the cost is transparency. Generating your own templates can be more involved than what most people may wish to take on–PHP is one language in particular that I’ve not had the best experiences with and it may not be obvious what includes may be required to get the functionality you’d like if you’re trying to write your own template or plugin. Some CMS systems also won’t make rolling back changes terribly easy. Perhaps you broke something or accidentally deleted a post? You may be able to do a restore but there are potentially other changes that were made in the interim. Or maybe you can’t track if someone else made that change. Certain CMS may be better about this but it’s still doing a lot and that brings up the last con–they’re big. They’re relatively complicated systems that are constantly running jobs in the background and dynamically inserting their content and they can end up requiring more resources than would be ideal or just being generally slow. They are also commonly targeted for exploitation and with so many mechanisms, and the administration component being accessible from the Internet, there is generally a good amount of attack surface.

Hosted Web Services

Getting away from dynamic systems we have the OG static site. Again, showing my age, but in the Geocities era, if you didn’t pay for a cgi-capable hosting service, this is all you had. Hosted services have come down in price and gone up in capability significantly and many of the drawbacks that will be mentioned are addressable but they do require effort and may still carry forward their limitations. With many common plans for various hosting providers you can very easily integrate buying a domain and hosting as well as being able to run dynamic content (commonly PHP) within reasonable constraints for a low-cost plan.

You interact with a hosted web service using SFTP (FTP over SSH) which is much more secure than the previously-ubiquitous plain-text FTP protocol however authenticating is generally limited to username and password. Managing users for a hosting provider’s account can be tedious at best (non-existant at worst) and doesn’t have a lot of intelligence or technology behind it, which is what spawned the all-in-one nature of the modern CMS.

Enter the Modern Static Site

This isn’t new but it is, probably, the newest pattern to build a good site. There is an entire movement behind building these sites over at jamstack.org and, really, it makes a lot of sense. The J, A, and M in “Jamstack” stand for JavaScript, APIs, and Markup. JavaScript the enables rich and interactive experiences of the web and is able to interact with APIs to enhance sites and web apps with dynamic content. Markup is the language used to turn the pre-generated content, like this post, into a web site, without fiddling with HTML tags and the like. There’s value, as a business, in having generated static sites as they reduce the attack surface on the front end and allow for collaborative work using git in addition to the ability to heavily automate content generation. There is value, as an individual, in generally reducing complexity and making it easier to have a functional website.

There are a number of places that support deploying static sites like Netlify, GCP, AWS, and Azure. There are also a number of generators like Jekyll, Gatsby, and Hugo. The different platforms provide various services, supported generators, and integrations. The various generators provide support for various features and run on a (usually) specific programming language platform (e.g. Hugo is built with Go, get it? HuGo? and it uses the Go templating language) so you can find one that is geared to your wants and needs. I’ve chosen Hugo because it was the most straight-forward to begin with. I’ve since needed to adjust and merge templates to create different kinds of content and incorporate a certain amount of functionality but those are posts for another day.

Deploying a Hugo Site on Azure

There’s a few ways to go about this and I may be taking the road less travelled using Azure DevOps. I do have a GitHub account but I also do enjoy some of the aspects of Azure DevOps so I’m going in this direction. You can use whichever method you think works best for you–there is validity in using a GitHub account for many reasons. That’s just not what I’ve done here.

Setting up local files

You can’t just click go–you need a couple of things set up. Firstly, you need to have an Azure account spun up. Then you’ll need to have Azure DevOps going–and that was basically a couple of clicks. You’ll need some familiarity with git and I recommend Learn Git Concepts Not Commands as a primer (or a refresher–not judging). I did this on a macOS device so I used homebrew to install everything. You can get it at brew.sh. There are a couple of things you may need:

  • git (obviously)
  • hugo (obviously)
  • Git Credential Manager from Microsoft (brew install git-credential-manager-core). This may also install .Net core–I already had it installed so I’m not completely certain. It’s not required but it makes this process very simple.

Ideally, you would git init the directory that you were going to use for your Hugo site so that when you did hugo new site you could just track everything from jump. If you’ve already created a Hugo site you can use the following commands to make it a repo and add everything in there:

git init
git add -A

Easy enough. If you don’t have any idea where to start with this technology but are interested in trying it out to see what the workflow is like I’ll drop a script in here to get your local up to speed. Some lines are taken from the Hugo Quick Start page.

mkdir HugoOnAzure
cd HugoOnAzure
git init
hugo new site HugoOnAzure
# we need a theme
git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke
# no need to be cute just swap out the URL in config.toml using an editor
sed -i -e "s/example.org/yourdomain.com/g" config.toml
hugo new posts/my-first-post.md
# this will generate some info for your first post file
# there's a lot to this but it's a quickstart
# make sure to set 'draft' to false or to remove that line
sed -i -e "s/true/false/g" posts/my-first-post.md
# add something in there. this is 100% not the best way to edit files
echo "Hello world!" >> posts/my-first-post.md 

Setting up Azure DevOps

  1. In the Azure DevOps portal, ensure you have an organization. From here, add a new project using that nifty + New Project button at the top right.
  2. Create a new repository by clicking the + right next to the name of your project under the Azure DevOps logo at the top left.
  3. If you didn’t uncheck “Add a .gitignore” then you’ll be greeted with an empty repo and no really good information on how to proceed. I’ll provide some of that information presently. Take note of the name of the organization that you created in Azure DevOps and copy the URL of the page you’re currently looking at because you’ll need this to set up the remote location for the repository. Replace “organization”, “project”, and “repo-name” with the values that you need.
git remote add origin https://organization@dev.azure.com/organization/project/_git/repo-name
git push -u origin --all
  1. If you did uncheck that then you’ll have some information on how to push an existing repository already filled in with your repo information. Copy and paste that stuff into your shell and you should be able to push everything up to Azure DevOps. This step is where the git credential manager comes in to play–it’ll open up a browser window for you to log in (2FA and all that if you have it set up and you should) and makes it very convenient to get authenticated.

Creating a static web app

Here, the metaphorical rubber meets the proverbial road. I’d create a resource group for this just for organization purposes but you do you.

  1. You’ll go ahead and type “Static Web App” in the search bar for the Azure Portal to get into the menu for creating the static web app. The important thing here is to select “Other” in the Source parameter as we are going to use Azure DevOps as the source. It’s set up by default for GitHub and this is a very good marketing move as it’s extremely likely that a lot of people are coming in from GitHub but unless your org is paying for GitHub it’s more likely that they’ve got Azure DevOps. The free tier is pretty beefy so jump on into it.
  2. Click on the Go To Resource button (yeah, I know) and, when you get there, click on Manage Deployment Token in the top menu for the “Overview” screen. If you leave the “Overview screen to look around this will disappear so keep your eyes peeled.
  3. Go back to your repository in Azure DevOps and in the “Files” pane of the repository you should have a Set up build button at the top right (since you did definitely commit some files and push to your remote location in git like I mentioned in Setting up local files, right?).

Digging into the pipeline

Now it’s about to get real. We need to do a couple of scripty things. If you think this looks like a Docker buildfile I won’t disagree with you. The philosophy is the same here–a disposable environment that can build the thing. The thing, in this case, is our Hugo site. Based on your content and themes you may not need much at all. Some themes, however, require a little extra. That’s up to you to handle.

💡 As an aside, this looks a lot like a Docker buildfile and I’ve done a tiny bit of work on that before. Personal project, really, but it was tied into the WNYC Audiogram project (which I’m sure has been forked to hell with no credit) that I hoped to extend to support subtitles (haven’t gotten around to it). I’ve written about it on Medium @mark_diaz.

We need to drop that token in there as a variable. Go ahead and do that and name it TOKEN. A sturdy name for a sturdy token. I chose to keep the token a secret and we’ll see whether or not it will bite me in the ass.

To write the pipeline file YAML script… well. The best way to do that, in my experience, is to see a working example. I grabbed this one from Unravelled Development. They’ve got a nifty feature here with a staging token and you should check out their post for more info on that (it’s very good):

trigger:
- main

stages:
  - stage: 'GenerateHugo'
    displayName: 'Generate hugo website'
    jobs:
      - job:
        pool:
          vmImage: ubuntu-latest
        workspace:
          clean: all
        steps:
        - checkout: self
          displayName: 'Checkout repository including submodules'
          submodules: true  # true so Hugo theme submodule is checked out
        - script: wget https://github.com/gohugoio/hugo/releases/download/v0.83.1/hugo_0.83.1_Linux-64bit.deb -O '$(Pipeline.Workspace)/hugo_0.83.1_Linux-64bit.deb'
          displayName: Download Hugo v0.83.1 Linux x64
        # 2. Installs Hugo executable
        - script: sudo dpkg -i $(Pipeline.Workspace)/hugo*.deb
          displayName: Install Hugo
        - task: AzureStaticWebApp@0
          condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
          inputs:
            app_location: '/'
            app_build_command: 'hugo'
            output_location: '/public'
            azure_static_web_apps_api_token: '$(TOKEN)'
        
        - task: AzureStaticWebApp@0
          condition: ne(variables['Build.SourceBranch'], 'refs/heads/main')
          inputs:
            app_location: '/'
            app_build_command: 'hugo'
            output_location: '/public'
            azure_static_web_apps_api_token: '$(STAGING_TOKEN)'

# below added by Mark
# really good stuff here. Important to make sure that the submodules are checked out as that is the recommended way to store themes.
# themes are worth discussing but that's more complicated than this post needs to be. I'll get there, I promise.
# in plain text this build file checks out the repo, downloads and installs hugo on an ubunt container, and then 
# builds your source material with the hugo installed in the container and deploys it to your web app using 
# that token we dropped in there as a variable.

So I got a fun error: ##[error]No hosted parallelism has been purchased or granted. To request a free parallelism grant, please fill out the following form https://aka.ms/azpipelines-parallelism-request. This feels like the Azure equivalent of “McFly, you bozo, those boards don’t work on water. Unless you’ve got powwwwaah.” I submitted a request but I don’t know when it will come through. I’m writing this stream of consciousness now to see what the feeling of it is like. I’m not even sure what it means by “parallelism” because this seems relatively straightforward but maybe I’m missing something. After searching for a while it seems that you need to request “parallelism” for a private repo via that form. This script doesn’t feel terribly parallel but this is free-tier so it is what it is. Allegedly this can take days to be addressed.

30 MINUTES LATER Booooring. I know I just submitted the form about 20-30 minutes ago but yes, it’s very boring. The waiting, that is. Quite dull and uneventful. I haven’t come up with any great ideas just yet–waiting to get this website up. Yes, I’m using this process to make this very same website. Talk about dogfooding.

I’m actually very big on dogfooding. Not eating one’s dog’s food but “eating one’s own dogfood” which is an idiom that basically equates to “suffer what you cause others to suffer.” There is a symmetry there. An inherent empathy. I dig it. This will give me time to figure out the font problem I seem to be having with this template. It is very likely of my own making.

💡 At some point in here I got an error that the remote repository was ahead of my local. That’s not a good problem to have. I needed to do git config pull.rebase false and then do a git.pull on it to make a git push work. Just a heads up. Also, it’s been a few hours and still waiting on that form to come back to me for my free tier of Azure DevOps build. Seriously, team, I know we want to prevent abuse but also c’mon now.

Setting up a self-hosted agent

ONE DAY LATER Ok, I’m going to install a self-hosted agent. I have a Linux box that’s hanging out and running my asterisk server. It’s idle most of the time that it’s on so why not? Grabbing the agent from the page Deploy an Azure Pipelines agent on Linux. The vibe here is a lot like a Jenkins agent, if you’ve ever deployed one of those. The concepts here aren’t new but they are, possibly, put together in a novel manner. There’s a couple of things to do and several considerations to make–most of them are on that page but I’ll drop a script here to handle the files.

# this URL may change--the page linked above will have the latest
wget https://vstsagentpackage.azureedge.net/agent/2.196.1/vsts-agent-linux-x64-2.196.1.tar.gz
sudo adduser vstsagent #this is a little bit of a process but it should be straightforwward
cd /home/vstsagent
sudo mkdir agent
cd agent
sudo tar -xvf vsts-agent-linux-x64-2.196.1.tar.gz

The files are good to go in the home directory of the user vstsagent (ymmv based on what flavor you’re using but this is pretty common stuff nowadays). You’ll need a personal access token to register the agent. You can click on the person icon at the top right of the page in Azure DevOps and go down to personal access tokens. From there expand all scopes and pick Agent Pools (read and manage). If this seems a bit pedantic… it isn’t. It’s extremely important to understand that a PAT is basically a login and password put together. So yes, please ensure that the scope here is correct and don’t go making PATs at random. Also, and I know it tells you but it bears repeating. Save the PAT until you’re done with it (thanks, Blake Snyder) because you can’t get it back. You’ll need to generate another one.

su vstsagent # you remember the password you gave this user, right?
./config.sh # it's got ascii art it's legit. loving the resurgence of the command line
💡 When it asks you to join a pool and says Default it really means it will put the agent in the pool called Default. If you made a pool already go ahead and type its name in there. I now have a pool called Default and that’s fun, I guess. Going through the configuration process again you might be able to move it. At this stage it isn’t important but it’s worth noting in case you’re running up against issues

We are now presented with the choice of running it as a service or on demand. I’m going to go ahead and run it as a service and I will do that by executing sudo ./svc.sh install vstsagent and it will install and run as the user vstsagent which is not privileged on this device. You should definitely take a look at the script before running it in the event that some element there doesn’t work out for you or needs to be adjusted. You also may need to exit the vstsagent user session as, at least how I want it, that account cannot sudo. I didn’t have any issues and was able to start the agent manually by typing sudo ./svc.sh start. It should automatically start on reboot.

Go back to your project and into Project Settings (it’s at the bottom left). Ensure that whatever pool you added this agent to is available to the project. I haven’t tried running the pipeline again yet but I’m hoping it would figure out that I had an agent available. Ok it complained and it still tried to go to the hosted pipeline. I added the following to the YAML file.

pool:
  name: Default
  # this 'pool' section already exists in the yaml posted above. 
  # just add the name entry to it
  # this name may also be case-sensitive. 

It’s now asking for permission to access a resource. It will let you know in the pipelines area and you can permit it with a click which is nice. It looks like this is to ensure you don’t run the pipeline on something you shouldn’t be.

Ok great the bash script tanked. Let me actually look at this pipeline and adjust it. I may already have Hugo installed on that system anyway. The error was at the installing Hugo step and that makes a whole lot of sense since it didn’t seem to spin up the docker image like I thought it would. So now it failed at running Docker.

Starting: AzureStaticWebApp
==============================================================================
Task         : Deploy Azure Static Web App
Description  : [PREVIEW] Build and deploy an Azure Static Web App
Version      : 0.194.2
Author       : Microsoft Corporation
Help         : https://aka.ms/swadocs
==============================================================================
/bin/bash /home/vstsagent/agent/_work/_tasks/AzureStaticWebApp_18aad896-e191-4720-88d6-8ced4806941a/0.194.2/launch-docker.sh
docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/create": dial unix /var/run/docker.sock: connect: permission denied.
See 'docker run --help'.
##[error]Error: The process '/bin/bash' failed with exit code 126

Good stuff. I already added vstsagent to the docker group with sudo usermod -a -G docker vstsagent. Let’s see if a therapeutic reboot may help.

Reboot did the trick. I probably just needed to restart systemctld but I took the easy way out. Pipeline works! So that’s how you make a pipeline and it feels good to be green But there’s still a few more things we need to do to make it work work. The website is there but we need to adjust a few things because our static web app has a boosted URL.

Summary

A lot happened in this post. We got set up with Azure DevOps and a local set of files to then put in to a repository in Azure DevOps. We created a Static Web Application. We then created a build pipeline to build the website and then deploy it. We also created the website and there has been few modifications from how it originally came (at the moment). There will be some posts on those. We had to take a detour for the CI agent as the free-tier agent takes manual approval of a request, so we went ahead and installed a Linux agent on a box I had at home. We also created a repository and checked in code (this actual website that you’re reading right now).

Good stuff. As to what’s next? Well, if you were following along and have now gone to visit your Hugo site you’ll find that it doesn’t look great. Style sheets aren’t loading, etc. It’s there but it isn’t quite functional. That’s what’s coming next.

Actually, found another problem

As I finished typing this, staged, committed, and pushed–I got another build failed. It succeeded a few hours ago.

1 error(s), 0 warning(s)
    One or more errors occurred. (One or more errors occurred. (Access to the path '/home/vstsagent/agent/_work/1/s/public/posts/post-6/index.html' is denied.)) (One or more errors occurred. (Access to the path '/home/vstsagent/agent/_work/1/s/public/posts/index.html' is denied.)) (One or more errors occurred. (Access to the path '/home/vstsagent/agent/_work/1/s/public/posts/index.xml' is denied.)) (Access to the path '/home/vstsagent/agent/_work/1/s/public/posts/post-6/index.html' is denied.) (Access to the path '/home/vstsagent/agent/_work/1/s/public/posts/index.html' is denied.) (Access to the path '/home/vstsagent/agent/_work/1/s/public/posts/index.xml' is denied.)

Let’s take a peek.

drwxr-xr-x 15 vstsagent vstsagent 4.0K Dec 15 17:19 .
drwxr-xr-x 10 vstsagent vstsagent 4.0K Dec 15 17:19 ..
drwxr-xr-x  2 root      root      4.0K Dec 15 12:15 about
drwxr-xr-x  2 root      root      4.0K Dec 15 12:15 about-mark
drwxr-xr-x  2 root      root      4.0K Dec 15 12:15 about-us
drwxr-xr-x  2 root      root      4.0K Dec 15 12:15 archives
drwxr-xr-x  2 vstsagent vstsagent 4.0K Dec 15 12:14 categories
drwxr-xr-x  2 root      root      4.0K Dec 15 12:15 contact
drwxr-xr-x  2 vstsagent vstsagent 4.0K Dec 15 12:14 css
drwxr-xr-x  2 vstsagent vstsagent 4.0K Dec 15 12:14 fonts
drwxr-xr-x  4 root      root      4.0K Dec 15 12:14 images
drwxr-xr-x  2 vstsagent vstsagent 4.0K Dec 15 12:14 js
drwxr-xr-x  5 vstsagent vstsagent 4.0K Dec 15 12:15 page
drwxr-xr-x 11 root      root      4.0K Dec 15 12:15 posts
drwxr-xr-x 12 vstsagent vstsagent 4.0K Dec 15 17:19 tags

I’ve been seeing some folders still have ownership of root/root. They still have read access so it should be fine but it’s also unusual. Perhaps we didn’t clean up well behind ourselves. I’m actually remembering reading something about how the non-Azure agents can cache things and I’m getting the vibe that this is the issue.

I say that yet the pipeline file already has clean: all under workspace which should very well be clearing any artifacts. What is going on? Well, clicking on the actual Job in the jobs area of the pipeline (drill down twice on “Job”) will tell you exactly which step failed and, in this case, it’s the “preparing build environment”. It looks like those permissions are not allowing for the build area to be cleaned. So let’s see if we can clean up after ourselves sufficiently.

root@markcentre:~# cd /home/vstsagent/
root@markcentre:/home/vstsagent# ls
agent  vsts-agent-linux-x64-2.196.1.tar.gz
root@markcentre:/home/vstsagent# cd agent
root@markcentre:/home/vstsagent/agent# ls
bin  config.sh  _diag  env.sh  externals  run-docker.sh  run.sh  runsvc.sh  svc.sh  _work
root@markcentre:/home/vstsagent/agent# cd _work
root@markcentre:/home/vstsagent/agent/_work# ls
1  2  node_modules  SourceRootMapping  _tasks  _temp  _tool
root@markcentre:/home/vstsagent/agent/_work# cd 1
root@markcentre:/home/vstsagent/agent/_work/1# ls
a  b  s  TestResults
root@markcentre:/home/vstsagent/agent/_work/1# cd ..
root@markcentre:/home/vstsagent/agent/_work# mv ./1 ..
root@markcentre:/home/vstsagent/agent/_work# ls
2  node_modules  SourceRootMapping  _tasks  _temp  _tool
root@markcentre:/home/vstsagent/agent/_work# mv ./2 ..

I moved the folders in question up just to make sure I had them if I still needed them. When those folders are gone the build executes just fine. It feels like there’s some contention as to the context of the file creation/modification. It would probably be best to just get rid of everything prior to finishing the task, but after deploying. Let’s take a look at that YAML pipeline file and see what we can do.

Investigating the pipeline

To be clear, this shouldn’t need to happen but we’re operating under specific circumstances. This is both the good part and the bad part about IT. In this situation I am not running the Azure build agent as root. To be even more specific–I’m not running a container in my build either however the AzureStaticWebApp task runs a file called launch-docker.sh to do the magic that it does. Even though I don’t need a container to build the site. It’s pretty cool that it runs a docker image with Oryx to actually do all the building of the image. This allows for a very modular approach where the code-building black box can be updated independently and I dig it.

This isn’t transparent to the end-user in the Azure DevOps pipeline, however, but it was interesting to learn regardless. Docker, by default, will write to disk as root which is where I’m running into permissions issues. It turns out that docker run has an argument for user. Perhaps this will work. I adjusted the launch-docker.sh to have that –user argument:

docker run --user "$(id -u):$(id -g)"\
    --env-file ./env.list \
    -v "$SWA_WORKING_DIR:$SWA_WORKSPACE_DIR" \
    "$SWA_DEPLOYMENT_CLIENT" \
    ./bin/staticsites/StaticSitesClient run

In using the id command it should, theoretically, Just Work. It did not. Popped off with an “unexpected error” once it was in the container. I’m getting the vibe that the container needs root for something that it does. That’s annoying.

Resolution or acceptance

It turns out that Oryx will throw an unspecified error any time the build script is run on a container with a non-root user specified in –user and that’s unfortunate. I’ll be submitting an enhancement request for that. I did work around this, though. The “straightforward” way is to simply just not build the application in the container but still use it to deploy the app. I haven’t actually drilled down into what that process is like so I’m still using the black box AzureStaticWebApp task. But I have an entire Linux box. The pipeline originally started with actually installs Hugo (like it would do on a container) but I’ve found out two things: since I’m not running the agent as root I simply can’t install or update Hugo as part of this process and I can do it with Homebrew. Well, I can install it with Homebrew, at least. So I did.

Aaaaand it didn’t work. ##[error]Bash exited with code '127'. is what I got. Turns out that when command line tasks run they run with –noprofile and –norc and that’s what Homebrew uses to set up your path (Homebrew sets everything up in the user’s folders and doesn’t require root). Ok cool no problem we just use the full path. That works but how do we get around the AzureStaticWebApp step? There’s a parameter called skip_app_build that will do it for us. For that step you can see something approaching the following in the output:

App Directory Location: '/public' was found.
No Api directory specified. Azure Functions will not be created.
Looking for event info
Skipping step to build /working_dir/public with Oryx
Either no Api directory was specified, or the specified directory was not found. Azure Functions will not be created.
Zipping App Artifacts
Done Zipping App Artifacts
Uploading build artifacts.
Finished Upload. Polling on deployment.

It’s the final pipeline 🎹

trigger:
- main

stages:
  - stage: 'GenerateHugo'
    displayName: 'Generate hugo website'
    jobs:
      - job:
        pool:
          name: Default

        workspace:
          #clean: all

        steps:
        - checkout: self
          displayName: 'Checkout repository including submodules'
          submodules: true  # true so Hugo theme submodule is checked out

        - task: CmdLine@2
          displayName: "Hugo build"
          #command line runs with --noprofile --norc and that's annoying
          inputs:
            script: '/home/vstsagent/.linuxbrew/bin/hugo'
        
        - task: AzureStaticWebApp@0
          condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
          inputs:
            skip_app_build: true 
            app_location: '/public'
            app_build_command: ''
            output_location: ''
            azure_static_web_apps_api_token: '$(TOKEN)'

If “The Final Countdown” is playing in your head then I have succeeded.

Lessons from the Pipeline

The real pipeline is the challenges we discovered along the way. You’ll note that the staging task is not present and that’s in the interest of clarity as well as because I don’t have a staging site up just yet. That’s going to tie in with the other stuff in the next post. This pipeline also will only work on a self-hosted agent. This might be what you need to do for reasons (in my case it was because I didn’t have that delicious free-tier grant) but it can really be a pain in the ass. It’s almost always better to KISS and this is entire post is an intentionally self-inflicted example of that. Not running the agent as root is really what made this difficult–not even because the build didn’t finish but because it couldn’t clear out the artifacts for the next build. Hugo, itself, doesn’t clear out that directory either so just rolling with it wouldn’t be advisable–you might end up using more storage than necessary. You could have a cron job continuously chowning the directory. You could spin up a container to clean the directory and then the container to deploy. You could do a lot of things but they’re all elements that add complexity. So keep it simple, stupid. If you don’t have an actual reason to run a self-hosted agent, don’t. Sometimes money is that reason and that’s ok. As long as there is a reason.

The real lesson, though, is to read better. Really read things. I read a few Issues on the Hugo Github about cleaning the output directory (and this makes total sense to me as well as other people) and they basically said “Hugo doesn’t clean those files. Figure it out, nerd.” I’m embellishing, but only slightly. I didn’t continue looking because I mistook confidence for fact. The Hugo Basic Usage page mentions a “flag” and yes, reader, there is indeed a --cleanDestinationDir flag. I genuinely don’t know how I missed that flag searching for ‘hugo clean’ but now that I’ve seen it it’s coming up ahead of the Issues pages. Thanks, search engines.

What’s happening now is that /public can’t be cleared when the repo checks out so I need to finally add that directory to my .gitignore. I have seen plenty of /public directories in repos for themes but I don’t really need to have it tracked in versioning (a statement I may very well regret at a later date). Just adding that to the build command (re-enabling the build in the AzureStaticWebApp task) and removing the workspace cleaning stuff. Since it’s a docker container that’s building the app I’m not worried about cruft in the OS. There are actually several ways that the repo gets cleaned and I’m just going to go through all of them and make sure they’re disabled. It didn’t work, regardless. I got a chunky new error: ##[warning]Unable move and reuse existing repository to required location.. I’m going to submit that enhancement request to be able to pass a script or something to the Oryx container so that we can clean up after ourselves if need be. We’ll see how snarky people get about it.

If you stuck with me for this roller coaster (or if you were yelling at me because you already thought of a solution), thanks! It’s been fun. Now on to part 2 where I’m gonna fix the site up a little more. Make it feel like home. 14 errors, 119 warnings and 0 suggestions

Hi, this post was checked with vale which is a content-aware linter. It was checked using the Microsoft style as well as some rules that I made. A summary of those results is below. More details as to how this was put together check out this post. This post had: 26 errors, 206 warnings and 0 suggestions For details on the linting of this post
 ./content/posts/setting-up-this-website.md
 10:283    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 10:356    warning  Consider removing 'fairly'.     Microsoft.Adverbs      
 10:401    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 17:62     warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 17:90     warning  Consider using 'before'         Microsoft.Wordiness    
                    instead of 'prior to'.                                 
 17:119    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 17:170    warning  Use first person (such as       Microsoft.FirstPerson  
                    'my') sparingly.                                       
 20:4      warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 20:16     warning  Use first person (such as       Microsoft.FirstPerson  
                    'my') sparingly.                                       
 20:24     warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 20:134    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 20:186    warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 20:335    warning  Consider removing 'generally'.  Microsoft.Adverbs      
 20:373    error    Use 'they're' instead of 'They  Microsoft.Contractions 
                    are'.                                                  
 20:382    warning  Consider removing 'very'.       Microsoft.Adverbs      
 20:465    warning  Consider removing 'quickly'.    Microsoft.Adverbs      
 20:477    warning  Consider removing 'easily'.     Microsoft.Adverbs      
 20:733    warning  Consider using 'all' instead    Microsoft.Wordiness    
                    of 'all of'.                                           
 20:771    warning  Consider removing 'very'.       Microsoft.Adverbs      
 20:869    warning  Consider removing 'generally'.  Microsoft.Adverbs      
 22:408    warning  Consider removing 'terribly'.   Microsoft.Adverbs      
 22:454    warning  Consider removing               Microsoft.Adverbs      
                    'accidentally'.                                        
 22:531    warning  Consider removing               Microsoft.Adverbs      
                    'potentially'.                                         
 22:966    warning  Consider removing 'generally'.  Microsoft.Adverbs      
 22:982    error    Use 'they're' instead of 'They  Microsoft.Contractions 
                    are'.                                                  
 22:1138   warning  Consider removing 'generally'.  Microsoft.Adverbs      
 25:35     warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 25:35     error    Use 'we've' instead of 'we      Microsoft.Contractions 
                    have'.                                                 
 25:68     error    More than 3 commas!             marktoso.TresComas     
 25:78     warning  Use first person (such as       Microsoft.FirstPerson  
                    'my') sparingly.                                       
 25:466    warning  Consider removing 'very'.       Microsoft.Adverbs      
 25:471    warning  Consider removing 'easily'.     Microsoft.Adverbs      
 27:100    warning  ' previously-' doesn't need a   Microsoft.Hyphens      
                    hyphen.                                                
 27:173    warning  Consider removing 'generally'.  Microsoft.Adverbs      
 30:20     error    Use 'it's' instead of 'it is'.  Microsoft.Contractions 
 30:184    warning  Consider removing 'really'.     Microsoft.Adverbs      
 30:222    error    More than 3 commas!             marktoso.TresComas     
 30:793    warning  Consider removing 'heavily'.    Microsoft.Adverbs      
 30:867    warning  Consider removing 'generally'.  Microsoft.Adverbs      
 32:383    error    Use 'for example' instead of    Microsoft.Foreign      
                    'e.g.'.                                                
 32:485    error    Use 'that's' instead of 'that   Microsoft.Contractions 
                    is'.                                                   
 35:40     warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 35:131    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 35:148    warning  Consider using 'some' instead   Microsoft.Wordiness    
                    of 'some of the'.                                      
 35:187    warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 38:350    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 38:538    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 41:150    warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 41:224    warning  Consider removing 'very'.       Microsoft.Adverbs      
 69:96     warning  Consider removing 'really'.     Microsoft.Adverbs      
 69:276    warning  For a general audience, use     Microsoft.GeneralURL   
                    'address' rather than 'URL'.                           
 69:403    error    Punctuation should be inside    Microsoft.Quotes       
                    the quotes.                                            
 69:419    error    Punctuation should be inside    Microsoft.Quotes       
                    the quotes.                                            
 74:436    warning  Consider removing 'very'.       Microsoft.Adverbs      
 78:1      error    Use 'we're' instead of 'we      Microsoft.Contractions 
                    are'.                                                  
 78:244    warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 78:342    warning  Consider removing 'very'.       Microsoft.Adverbs      
 78:375    warning  Consider removing 'extremely'.  Microsoft.Adverbs      
 79:47     warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 80:260    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 83:29     warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'We'.                         
 83:118    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 83:258    warning  Try to avoid using              Microsoft.We           
                    first-person plural like                               
                    'our'.                                                 
 85:153    warning  Consider removing 'really'.     Microsoft.Adverbs      
 85:216    warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 85:269    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 87:1      warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'We'.                         
 87:127    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 87:167    warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 87:177    warning  Consider using 'whether'        Microsoft.Wordiness    
                    instead of 'whether or not'.                           
 87:205    warning  Use first person (such as       Microsoft.FirstPerson  
                    'me') sparingly.                                       
 89:39     warning  In general, don't use an        Microsoft.Ellipses     
                    ellipsis.                                              
 89:77     warning  Use first person (such as       Microsoft.FirstPerson  
                    'my') sparingly.                                       
 89:120    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 89:359    warning  Consider removing 'very'.       Microsoft.Adverbs      
 135:3     warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 135:356   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 135:397   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 135:469   error    Use 'it's' instead of 'it is'.  Microsoft.Contractions 
 135:481   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 135:586   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 135:746   warning  Consider removing 'terribly'.   Microsoft.Adverbs      
 135:789   error    Use 'it's' instead of 'it is'.  Microsoft.Contractions 
 135:800   error    Use 'it's' instead of 'it is'.  Microsoft.Contractions 
 138:18    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 138:51    error    Use an en dash in a range of    Microsoft.RangeFormat  
                    numbers.                                               
 138:51    warning  In most cases, use 'from' or    Microsoft.Ranges       
                    'through' to describe a range                          
                    of numbers.                                            
 138:83    warning  Consider removing 'very'.       Microsoft.Adverbs      
 138:109   error    Use 'that's' instead of 'that   Microsoft.Contractions 
                    is'.                                                   
 138:144   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 138:231   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 138:268   warning  Consider removing 'very'.       Microsoft.Adverbs      
 140:1     warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 140:14    warning  Consider removing 'very'.       Microsoft.Adverbs      
 140:256   warning  Use first person (such as       Microsoft.FirstPerson  
                    'me') sparingly.                                       
 140:294   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 140:335   error    Use 'it's' instead of 'It is'.  Microsoft.Contractions 
 140:341   warning  Consider removing 'very'.       Microsoft.Adverbs      
 140:356   warning  Use first person (such as       Microsoft.FirstPerson  
                    'my') sparingly.                                       
 142:51    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 142:107   warning  Use first person (such as       Microsoft.FirstPerson  
                    'my') sparingly.                                       
 142:151   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 142:348   warning  Use first person (such as       Microsoft.FirstPerson  
                    'me') sparingly.                                       
 142:355   warning  Use first person (such as       Microsoft.FirstPerson  
                    'my') sparingly.                                       
 142:391   warning  Consider removing 'Seriously'.  Microsoft.Adverbs      
 142:407   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 142:415   warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 144:30    warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 145:21    warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 145:56    warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 145:62    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 145:113   warning  Use first person (such as       Microsoft.FirstPerson  
                    'my') sparingly.                                       
 145:198   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 145:245   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 145:391   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 145:469   error    Use 'they're' instead of 'they  Microsoft.Contractions 
                    are'.                                                  
 155:71    warning  Prefer 'YMMV' over 'ymmv'.      marktoso.Substitue     
 155:205   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 155:367   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'Agent'.                               
 155:428   warning  In general, don't use an        Microsoft.Ellipses     
                    ellipsis.                                              
 155:447   warning  Consider removing 'extremely'.  Microsoft.Adverbs      
 155:640   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 160:84    warning  Consider removing 'really'.     Microsoft.Adverbs      
 160:113   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 160:212   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 160:263   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 162:1     error    Use 'we're' instead of 'We      Microsoft.Contractions 
                    are'.                                                  
 162:1     warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'We'.                         
 162:79    warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 162:128   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 162:247   error    Use 'isn't' instead of 'is      Microsoft.Contractions 
                    not'.                                                  
 162:347   warning  Consider using 'if' instead of  Microsoft.Wordiness    
                    'in the event that'.                                   
 162:503   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 162:528   error    Use 'can't' instead of          Microsoft.Contractions 
                    'cannot'.                                              
 162:540   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 162:592   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 164:119   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 164:208   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 164:243   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 164:253   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 164:335   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 174:38    warning  Use first person (such as       Microsoft.FirstPerson  
                    'me') sparingly.                                       
 174:275   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 189:12    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 189:103   warning  Try to avoid using              Microsoft.We           
                    first-person plural like                               
                    'Let's'.                                               
 191:71    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 191:213   warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 191:274   warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 191:313   warning  Try to avoid using              Microsoft.We           
                    first-person plural like                               
                    'our'.                                                 
 191:346   warning  For a general audience, use     Microsoft.GeneralURL   
                    'address' rather than 'URL'.                           
 194:30    warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'We'.                         
 194:135   warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'We'.                         
 194:172   warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'We'.                         
 194:246   warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'We'.                         
 194:391   warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'We'.                         
 194:426   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 194:449   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 194:494   warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 194:530   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 194:544   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 199:3     warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 204:1     warning  Try to avoid using              Microsoft.We           
                    first-person plural like                               
                    'Let's'.                                               
 222:146   warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 222:188   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 222:283   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 224:1     warning  Use first person (such as 'I    Microsoft.FirstPerson  
                    ') sparingly.                                          
 224:90    warning  Consider removing 'very'.       Microsoft.Adverbs      
 224:127   error    Use 'what's' instead of 'What   Microsoft.Contractions 
                    is'.                                                   
 224:307   error    Punctuation should be inside    Microsoft.Quotes       
                    the quotes.                                            
 224:370   error    Use 'aren't' instead of 'are    Microsoft.Contractions 
                    not'.                                                  
 224:424   warning  Try to avoid using              Microsoft.We           
                    first-person plural like                               
                    'let's'.                                               
 224:437   warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 244:1     warning  Use first person (such as 'I    Microsoft.FirstPerson  
                    ') sparingly.                                          
 244:53    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 244:67    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 244:294   warning  Consider using 'before'         Microsoft.Wordiness    
                    instead of 'prior to'.                                 
 244:346   warning  Try to avoid using              Microsoft.We           
                    first-person plural like                               
                    'Let's'.                                               
 244:404   warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 247:50    warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 247:167   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 247:203   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 247:244   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 247:275   warning  Use first person (such as       Microsoft.FirstPerson  
                    'my') sparingly.                                       
 247:405   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 247:606   warning  Consider removing 'very'.       Microsoft.Adverbs      
 247:694   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 249:185   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 249:363   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 257:67    error    Use 'didn't' instead of 'did    Microsoft.Contractions 
                    not'.                                                  
 257:144   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 260:442   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 260:499   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 260:659   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 260:679   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 260:692   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 260:758   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 260:855   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 262:72    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 262:307   warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 262:356   warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 262:462   warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'us'.                         
 311:54    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 314:37    warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 314:100   error    Use 'isn't' instead of 'is      Microsoft.Contractions 
                    not'.                                                  
 314:171   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 314:328   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 314:385   warning  Use first person (such as       Microsoft.FirstPerson  
                    'my') sparingly.                                       
 314:407   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 314:465   warning  Consider removing 'really'.     Microsoft.Adverbs      
 314:714   warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 314:731   warning  Consider removing 'really'.     Microsoft.Adverbs      
 314:1336  warning  Prefer 'personal digital        Microsoft.Terms        
                    assistant' over 'agent'.                               
 316:45    warning  Consider removing 'Really'.     Microsoft.Adverbs      
 316:173   warning  Use first person (such as       Microsoft.FirstPerson  
                    'me') sparingly.                                       
 316:280   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 316:350   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 316:707   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 318:84    warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 318:125   warning  Use first person (such as       Microsoft.FirstPerson  
                    'my') sparingly.                                       
 318:205   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 318:214   warning  Consider removing 'really'.     Microsoft.Adverbs      
 318:271   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          
 318:278   warning  Consider removing 'very'.       Microsoft.Adverbs      
 318:295   warning  Consider using 'later' instead  Microsoft.Wordiness    
                    of 'at a later date'.                                  
 318:503   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 318:757   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 318:786   warning  Consider using 'all' instead    Microsoft.Wordiness    
                    of 'all of'.                                           
 318:962   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 318:1078  warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'we'.                         
 318:1122  warning  Try to avoid using              Microsoft.We           
                    first-person plural like 'We'.                         
 320:19    warning  Use first person (such as       Microsoft.FirstPerson  
                    'me') sparingly.                                       
 320:166   warning  Use first person (such as       Microsoft.FirstPerson  
                    'I'm') sparingly.                                      
 324:210   warning  Use first person (such as ' I   Microsoft.FirstPerson  
                    ') sparingly.                                          

26 errors, 207 warnings and 0 suggestions in 1 file.