Skip to content

Setting up the taskli.st blog

Posted on:December 29, 2022 at 09:22 AM

We created the taskli.st blog in just a few steps. We made sure that creating and publishing content is as convenient as possible and that the blog is accessible and fast to load.

Table of contents

Open Table of contents

Astro

The whole website is a Astro application. Astro is blazing fast and great for content and SEO.

We used Astro Paper as a template. You can see the similarities if you check out their example blog: https://astro-paper.pages.dev/

Deploying to AWS using S3 and Cloudfront

This blog is hosted on S3 and distributed. The official Astro website provides a great guide on how to deploy to AWS: Deploy your Astro Site to AWS

Note: In their guide they explain how to give Cloudfront access to your S3 bucket using an origin access identity (OAI). AWS recommends to use origin access control (OAC) instead. AWS has their own instructions on how to set that up: Restricting access to an Amazon S3 origin

In addition to the provided instructions we also set up a Route 53 record that points to the Cloudfront distribution.

CI/CD with Github Actions

Whenever we push a new blog post to our remote repository on Github the website is build and deployed automatically using Github Actions.

We execute the following steps:

  1. Checking out the repository and setting up node (Managed Github Actions actions)
  2. Installing dependencies with yarn install
  3. Building the Astro website. Therefore we have "build": "astro build" in our package.json
  4. Setting up AWS credentials
  5. Deploying to S3. Therefore we have "deploy": "aws s3 sync ./dist s3://[OUR_S3_BUCKET]" in our package.json
  6. Invalidating the Cloudfront cache using an Github Action from the community. We have to tell Cloudfront to clear its cache, because we deployed a new version of our blog. We invalidate the entire cache using PATHS: /*. It is not most effective to invalidate the entire cache if you have only changed a configuration that Cloudfront might not be concerned about. But we decided to rather always invalidate than to deliver stale content by accident.

Our entire Github Actions file looks like this:

name: "Build and deploy blog"
on:
  push:
    paths:
      - "blog/public/**"
      - "blog/src/**"
      - "blog/astro.config.mjs"
      - "blog/tailwind.config.cjs"
      - "blog/tsconfig.json"
      - "blog/package.json"
    branches:
      - master

defaults:
  run:
    # We are using a monorepo. All of our blog data sits in the /blog folder
    working-directory: ./blog

jobs:
  deploy-blog-to-production:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: "14.x"
      - name: Install dependencies
        run: yarn install
      - name: Build blog
        run: yarn build
      - name: Configure AWS credentials from Production account
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: eu-central-1
      - name: Deploy application
        run: yarn deploy
      - name: Invalidate CloudFront
        uses: chetan/invalidate-cloudfront-action@v2
        env:
          DISTRIBUTION: ${{ secrets.DISTRIBUTION_ID }}
          PATHS: "/*"
          AWS_REGION: "eu-central-1"
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

Creating cover and open graph images

For every blog post, we would like to have a specific image that we can use for the cover and for the open graph image.

We use DALL-E to generate images from AI. We pass a description of the blog post to the machine and then pick one of the returned images that is most fitting.

We resize the image using ImageMagick and run it in Docker.

For our cover image, we have a 2.44/1 ratio and we crop the image depending on what looks best for the individual image:

docker run -v /path/to/assets:/imgs dpokidov/imagemagick /imgs/authenticate-your-users-at-the-edge-with-lambda-at-edge-and-jwks-original.png -resize 732x732 -crop 732X300+0+250 -quality 50 /imgs/authenticate-your-users-at-the-edge-with-lambda-at-edge-and-jwks-blog.jpeg

For our open graph image, we crop the image to the recommended 1.91/1 ratio:

docker run -v /path/to/assets:/imgs dpokidov/imagemagick /imgs/authenticate-your-users-at-the-edge-with-lambda-at-edge-and-jwks-original.png -crop 1024X536+0+250 -quality 50 /imgs/authenticate-your-users-at-the-edge-with-lambda-at-edge-and-jwks-og.jpeg

We create a component that we use to display the cover image in our blog posts:

cover-image.tsx

const CoverImage: React.FC<{ alt: string; src: string }> = ({ src, alt }) => (
    <p>
        <img src={src} alt={alt} />
        <p
            style={{
                fontSize: "0.9rem",
                marginTop: "-25px",
                opacity: 0.7,
                textAlign: "center",
            }}
        >
            <em>{alt}</em>
        </p>
    </p>
);

export default CoverImage;