All related links: https://www.theurlist.com/autobloggerpost

Jekyll is a very popular way to host a static site on GitHub and posting new content is a simple as creating a markdown file and then committing it to the relative repo. GitHub takes care of the rest.

See the help pages at GitHub -> Setting up your GitHub Pages site locally with Jekyll

This seemed like a great way for me to get started on a blog for some content that was being stored in a CMS. The view we are interested in is updated once a week and is accessible via .NET Standard API.

There are a few options for making this happen. I could write a console application and execute it once a week to produce the file, then run through the git commands myself. Why would I do that?

What are we doing

Every Monday a new post must be created for the blog. instead of running the process manually using

dotnet run myapp
git add newfile.md
git commit -m 'add new file'
git push

I wanted to completely automate the process. Using Docker, Logic Apps, and Azure Container Instances (ACI); managing the when to "start, stop and clean up" was pretty easy. However, there isn't a full GitHub connector in Logic Apps for performing the commands in git. So, I added git to my container and a startup.sh script to get this done.

  • Logic Apps kicks off the process every Monday at 10:30 AM
  • ACI Container Image runs .NET Core Application
    • Clones repo
    • Calls CMS API for new data
    • Produces new Markdown file
    • Git commit, add, push
  • Logic App removes ACI Container
  • GitHub CI/CD runs to update the blog with new content
  • Logic App emails me
    • Email contains logs for the container (git output)
    • Asks for approval to Tweet announcement of new content
  • Logic App send new Tweet

Details, how was it done?

We will use the following components to build our Jekyll "autoblogger".

Docker container

Let's start with the container. It looks like an atypical .NET Core 2.2 image with the exception that git-core is installed in the final image.

FROM mcr.microsoft.com/dotnet/core/sdk:2.2 as build-env
WORKDIR /app

COPY autoblog/*.csproj ./
RUN dotnet restore

COPY ./autoblog ./
RUN dotnet publish -c Release -o out

# FINAL IMAGE
FROM mcr.microsoft.com/dotnet/core/runtime:2.2
WORKDIR /app
COPY --from=build-env /app/out .

# install git
RUN apt-get update && \
  apt-get upgrade -y && \
  apt-get install -y git-core

COPY ./autoblog/startup.sh .
RUN chmod 777 startup.sh
ENTRYPOINT ["/bin/bash", "./startup.sh"]

The startup.sh file is where the magic happens. When the container starts, we first configure git to use the deployment key stored as an environment variable ($token), clone the source repo into our working directory and re-init the repo (might be a bug only works if we do this).

Last, set the git user/email for any commits and then start the .NET Core console app.

# ensure the git deploy token is used for commits
git config --global url."https://$token:@github.com/".insteadOf "https://github.com/"

# clone the source repository
git clone $source /app/$dir

# bug? must re-init the repo
# set the git user/email for commits
cd /app/$dir && \
  git init && \
  git config --global user.email "$email" && \
  git config --global user.name "$user"

# run the process for creating new entry
dotnet /app/autoblog.dll

All of the ENV variables are set when the ACI container is started using the Logic App

.NET Core Application

The console application running inside of the container which creates the formatted markdown page, git commits and pushes to the repo.

Here is the Main() paraphrased code.

 static async Task Main(string[] args)
 {

   var root = $"/app/{Environment.GetEnvironmentVariable("dir")}";

   if (!Directory.Exists(root))
   {
     Directory.CreateDirectory(root);
   }

   var output = new System.Text.StringBuilder();
   output.AppendLine(Bash($"git -C {root} pull"));

   await GetContentRepositoryItemsAsync();

   var creator = new Creator.Post(ContentItems);
   try
   {
      var fileContents = await creator.Create();
      await File.WriteAllTextAsync(creator.FileName, fileContents);
   }
    catch (Exception ex)
   {
    Console.WriteLine(ex.ToString());
    throw ex;
   }

   output.AppendLine(Bash($"git add ."));
   output.AppendLine(Bash($"git commit -m '{creator.FileName} added'"));
   output.AppendLine(Bash("git push"));

   Console.WriteLine(output.ToString());
 }

The Creator class produces the file and once complete, we execute the git commands to push the newly created file the repo.

Azure Logic App

Azure Logic Apps enables the creation of powerful business workflows by integrating connectors for various services into a single graphical interface. With the connector for Azure Container Instances, you can run containerized applications as part of your Logic Apps workflow.

Azure Container Instances (ACI) is the easiest way to run containers in Azure. We can use it to run containers without worrying about provisioning infrastructure and we only pay for resources that are used.

In this case, we need to run the process every Monday at a set time to start the container process.

logic app overview

The container image is built and stored in my private Azure Container Registry and is easily configured in the "Create container group" connector.

logic app image1

The needed ENVIRONMENT variables can be configured here as well. Another option is to use the Azure Key Vault Connector to share and secure secret keys across apps.

logic app image ENV

Next, a variable complete is set to hold the status of the container. The Until task will run until the state of the container is Succeeded and complete is true. If false, we add a Delay task of 10 seconds.

logic app image condition

If the container group returns Succeeded, the logs are retrieved, the container group is cleaned up and an email approval is sent for the tweet.

logic app true

Wrapping up

Azure Logic Apps is a fun productive way to build process flows and in this case, I was able to produce content using a code, containers and approval process without having any manual steps.