Building a Blog - Part 3: CI and deployment

21 May 2020

Now that I've constructed my site and tested. I need an automated way to running and deploying autonomously.

CI

My repository is designed such that the content of my blog is seperated from the web project. It currently contain content and web, the latter containing the Gatsby project.

Therefore, after each time I add content, I will need CI to deploy that content, and each time I update web, CI needs to build and run my tests.

Deployment

While I could of had my deployment process deploy master each time. I wanted to pin my project to a version.

To do that, I checkout master, copy the content to a temp directory. Then I checkout the version I want and replace the content there with latest one.

I then push to Azure Blob Storage.


Building a Blog - Part 2: Testing

16 May 2020

Testing is important in software development, as it gives us confidence that our solution is working as expected, as well as, preventing bugs from appearing.

Jest

Introducing Jest. Jest is created by facebook, who also created React, which Gatsby is based upon. Therefore it made sense to also use it as my testing library of choice.

Configuration

Before we can start using Jest, we have to sort out some configuration. As of writing, this is my current configuration.

I will not go through it, but it's there as a reminder to me.

// jest.config.js
module.exports = {
    transform: {
        '^.+\\.jsx?$': `<rootDir>/test/jest-preprocess.js`,
    },
    moduleNameMapper: {
        '@core(.*)': `<rootDir>src/core$1`,
        '@post(.*)': `<rootDir>src/modules/post$1`,
        '@shared(.*)': `<rootDir>src/shared$1`,
        '.+\\.(css|styl|less|sass|scss)$': `identity-obj-proxy`,
        '.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': `<rootDir>/test/__mocks__/file-mock.js`,
    },
    testPathIgnorePatterns: [
        `node_modules`,
        `\\.cache`,
        `<rootDir>.*/public`,
        `__fixtures__`,
    ],
    transformIgnorePatterns: [`node_modules/(?!(gatsby)/)`],
    globals: {
        __PATH_PREFIX__: ``,
    },
    setupFiles: [`<rootDir>/test/loadershim.js`],
    snapshotSerializers: ['jest-emotion'],
};

As well as that, we need to setup mocks, which I put in test/__mocks__.

// file-mock.js
module.exports = 'test-file-stub';
// gatsby.js
const React = require('react');
const gatsby = jest.requireActual('gatsby');
module.exports = {
    ...gatsby,
    graphql: jest.fn(),
    Link: jest.fn().mockImplementation(
        // these props are invalid for an `a` tag
        ({
            activeClassName,
            activeStyle,
            getProps,
            innerRef,
            partiallyActive,
            ref,
            replace,
            to,
            ...rest
        }) =>
            React.createElement('a', {
                ...rest,
                href: to,
            })
    ),
    StaticQuery: jest.fn(),
    useStaticQuery: jest.fn(),
};
// react-helmet.js
const React = require('react');
const reactHelmet = jest.requireActual('react-helmet');
module.exports = {
    ...reactHelmet,
    Helmet: jest.fn(),
};

In order to get to get it working with Gatsby, we have to use a preprocessor.

// jest-preprocess.js
const babelOptions = {
    presets: ['babel-preset-gatsby'],
};
module.exports = require('babel-jest').createTransformer(babelOptions);

Testing

Jest's syntax is very similar to other Javascript testing frameworks. However, there is a feature which I really like. Snapshot Testing.

Your test will produce a snapshot, which you can commit into source control. Any future changes which modify the snapshot, which can be then reviewed.

// header.test.js
import React from 'react';
import renderer from 'react-test-renderer';
import Header from '../header';

const siteTitle = 'Blog';

describe('Header', () => {
    it('renders correctly', () => {
        const tree = renderer.create(<Header siteTitle={siteTitle} />).toJSON();
        expect(tree).toMatchSnapshot();
    });
});

Now if you run

jest

or if configured in package.json.

npm test

This produced the following snapshot, which I will commit.

// __snapshots__/header.test.js.snap
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Header renders correctly 1`] = `
.emotion-1 {
  background-color: #6b46c1;
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  -webkit-align-items: center;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  -webkit-box-pack: center;
  -webkit-justify-content: center;
  -ms-flex-pack: center;
  justify-content: center;
  -webkit-flex-wrap: wrap;
  -ms-flex-wrap: wrap;
  flex-wrap: wrap;
}

.emotion-0 {
  color: #fff;
  width: 100%;
  padding-left: 3rem;
  padding-right: 3rem;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}

@media (min-width:768px) {
  .emotion-0 {
    max-width: 640px;
    padding-left: 0.5rem;
    padding-right: 0.5rem;
  }
}

@media (min-width:1024px) {
  .emotion-0 {
    max-width: 768px;
  }
}

<header
  className="emotion-1"
>
  <div
    className="emotion-0"
  >
    <h1>
      <a
        href="/"
      >
        Blog
      </a>
    </h1>
  </div>
</header>
`;

If we make a change to code and we want to update the snapshot, the -u flag needs to be passed in.


Building a Blog - Part 1: GatsbyJS

30 Apr 2020

GatsbyJS is an open source static site generator, that is based on React.

I chose it for my blog, because all I needed were static pages, that can be generated via markdown, and then be stored somewhere in cloud to be served.

Getting started

You can install Gatsby via npm

npm install -g gatsby-cli

This gives you a handy CLI to develop against.

There are a lot of starter templates that you can find here. I went with the default one, gatsby-starter-default.

gatsby new my-default-starter https://github.com/gatsbyjs/gatsby-starter-default

Navigate into that directory, and start it up.

cd my-default-starter
gatsby develop

This will then be accessible at http://localhost:8000.

Now what?

Great, it's installed a bunch of things and I don't know what they do.

So, I decided to go and uninstall/delete files until I stripped it clean.

I went from

"dependencies": {
    "gatsby": "^2.21.0",
    "gatsby-image": "^2.4.0",
    "gatsby-plugin-manifest": "^2.4.0",
    "gatsby-plugin-offline": "^3.2.0",
    "gatsby-plugin-react-helmet": "^3.3.0",
    "gatsby-plugin-sharp": "^2.6.0",
    "gatsby-source-filesystem": "^2.3.0",
    "gatsby-transformer-sharp": "^2.5.0",
    "prop-types": "^15.7.2",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "react-helmet": "^6.0.0"
  }

to

"dependencies": {
    "gatsby": "^2.21.0"
    "react": "^16.12.0",
    "react-dom": "^16.12.0"
}

And then I started developing.

Plugins

Plugins are in the Gatsby are a great feature in the Gatsby ecosystem. They are tools which someone else has wrote to help you build your site.

gatsby-plugin-alias-imports

This was the first plugin I installed. It lets you reference modules with a custom alias, such as @post.

You would configure in gatsby-config.js, which is at the root of your directory.

const path = require('path');

module.exports = {
    plugins: [
        {
            resolve: `gatsby-plugin-alias-imports`,
            options: {
                alias: {
                    '@core': path.resolve(__dirname, 'src/core'),
                    '@post': path.resolve(__dirname, 'src/modules/post'),
                },
            },
        },
    ],
};

This would then point @post to src/modules/post.

gatsby-plugin-emotion

I wanted to use Tailwind CSS with Emotion.

This plugin, along with gatsby-plugin-postcss gave me the ability to style my components in js with tailwind.

const Title = styled.h1`
  ${tw`
    text-4xl lg:text-5xl
  `}
`

// We can then use it
<Title>{post.title}</Title>

gatsby-transformer-remark

And finally, I needed a plugin to transform my markdown files. This plugin along with gatsby-source-filesystem did that.

gatsby-remark-prismjs

This goes really well with the previous plugin as it uses PrismJS to format code blocks.

gatsby-plugin-react-helmet

This uses React Helmet to insert data into your document header, as Gatsby does not provide a native way of doing it.

<>
    <Helmet title="Blog - dctcheng" defer={false} />
    <Layout></Layout>
</>

It's also meant to help with SEO, but I haven't looked into that yet.

gatsby-plugin-sharp

Uses Sharp image processing library.

This package is consumed by gatsby-remark-images to extract images from inline markdown.

So thats about it, brief intro to Gatsby and it's plugins.


Hello, World!

23 Apr 2020

Introduction

Hello and welcome to my blog.

In my blog, it will mostly be filled will tech related contents, but also may contain some posts about my hobbies, such as board gaming, or sports.

Reason for this blog is for me to experiment with Gatsby and React, as well as place to store anything interesting that I come across, so I can refer back to it in the future.

Don't expect frequent posts, as I can guarantee you, they won't be frequent!

Creation

So originally, I was going to set up a .NET Core Web Api with a Vue front end and a Postgres database, which I unreliably host in cloud.

However, I thought if I was going to do this properly, I just need a way to serve static content, thus out goes the SPA and in comes Gatsby.

So my first set of posts will probably be about my journey in creating this blog.

© Copyright 2020, dctcheng. All Rights Reserved