Skip to content

Astro-Paper tips and tricks

Published: at 09:55 PM (6 min read)

I have been using the Astro-Paper theme for a few days now, this is documenting my Tips and Tricks.

Table of contents

Open Table of contents

Dates

I knew that I would struggle to remember to set the dates of new posts and to update them when I add to a post so I looked at ways to automate it.

I found this blog post that has a hook to automate updating the frontmatter on other markdown posts. I took this and tweaked it to add the pubDatetime if it is a new post and adds the modDatetime if the file has been modified. There is a PR open on the main repo or the code is in my repo.

If the hook is not what you are after then I have a solution for the new posts date, snippets. I have created a snippet for the frontmatter and the template for the page. They are nested so it does need two key presses but keeps the code DRY. Again there is a PR open on the main repo or you can find it on mine.

The modDatetime key needs to be left in the frontmatter, but this is not allowed to be null as it currently stands. The modDatetime: definition in the src/content/config.ts file needs to have .nullable() added to it, so it looks like modDatetime: z.date().optional().nullable().

Deploying to GitLab Pages

I am looking to get more proficient on GitLab CI so I wanted to deploy the application using it to their Pages offering. I have used the NPM scripts that are offered by the author to do some checks, then bundle the application to be deployed. I have posted a PR and it is also in my repo too.

Stages

I have included 3 stages (plus I am using the built in .pre stage)

stages:
  - code-style
  - build
  - deploy

Setting some Defaults

I am using this block to set the default docker image and the scripts needed to get the code ready for the main stage functions.

npm ci is used to remove any current node-modules, use the package-lock file so it is repeatable and some other bits for a CI run over a dev install.

--cache .npm --prefer-offline sets the build to use the cached version of the installation rather than downloading them each time.

default:
  image: node:20.10.0
  before_script:
    - npm pkg delete scripts.prepare
    - npm ci --cache .npm --prefer-offline

Caching

This sets the global cache config and the & allows the config to be referenced later on and modified. Not sure if this is 100% necessary but I was referencing a few different sources to get the parts to work.

I am using the hash of the package-lock file as the key, so it will only get the cache when it matches the lock file, another level of protection.

The .npm directory is where we are storing the npm level cache. Usually it is stored at ~/.npm but this is not cacheable in GitLab so needs to be relocated.

By default we want the cache to be read (or pull) only.

cache:
  - &global_cache_node_mods
    key:
      files:
        - package-lock.json
    paths:
      - .npm/
    policy: pull

Stage 0

.pre is a GitLab default stage and runs before anything else and run the ci install.

With this stage we are wanting to push to the cache as it will it only run when the cache needs to be updated, so we are pulling in the config and overwriting the policy to allow pushing and to only do it on successfully running the stage.

Again we are using the .npm directory and storing it as an artefact. I don’t think this is needed but does no harm (as far as I can tell).

This stage, as alluded to earlier, will only run when there has been a change that will make the cache invalid. Using only.changes: <FILE> means this will only run when there is a modification to the package.json file.

install_dependencies:
  stage: .pre
  script:
    - npm run ci
  cache:
    - <<: *global_cache_node_mods
      when: on_success
      policy: pull-push
  artifacts:
    paths:
      - .npm
  only:
    changes:
      - package.json

Stage 1 - Code Style Checks

There are 2 defined npm commands for the repo and we are using them here to do our code style checks, with some logging.

lint-job:
  stage: code-style
  script:
    - echo "Starting to lint the application..."
    - npm run lint
    - echo "Finished linting the application..."

format-job:
  stage: code-style
  script:
    - echo "Starting to format check the application..."
    - npm run format:check
    - echo "Finished format checking the application..."

Stage 2 - Build it

This could probably be merged with stage 3, but I liked the idea of keeping them separate in case there was anything else I wanted to do first, like changelogs or IoC.

On a successful run we are pulling the dist dir and calling it astro-paper-blog and storing it for 30 days.

build-job:
  stage: build
  script:
    - echo "Building the code..."
    - npm run build
    - echo "Build complete."
  artifacts:
    name: "astro-paper-blog"
    when: on_success
    expire_in: "30 days"
    paths:
      - "dist/*"

Stage 3 - Deploy it to GitLab Pages

GitLab Pages deployment has to be done from a job called pages, and we are defining this as the production release.

We are relying on the job before for its artefacts so define the dependency.

We do not need the code for this step so we can tell it that we do not need to run the check out with variables: GIT_STRATEGY: none. As we don’t have the code we can’t run the install, so overwrite the default before_script.

We already have the built artefact, but Pages needs it to be in a dir called public, so move it and then save it.

Any of the other stages can happen whenever, but the deploy should only happen from the main branch.

pages:
  stage: deploy
  environment: production
  dependencies:
    - "build-job"
  variables:
    GIT_STRATEGY: none
  before_script:
    - ""
  script:
    - mv dist public
  artifacts:
    paths:
      - public
  only:
    - main

Reading time

I saw the post in the main repo about adding reading time to the posts. The guide did well to document the parts needing to be updated, other than the tag part.

In src/utils/getPostsByTag.ts I needed to add await to getSortedPosts and async to the main function.

import type { CollectionEntry } from "astro:content";
import getSortedPosts from "./getSortedPosts";
import { slugifyAll } from "./slugify";

const getPostsByTag = async (posts: CollectionEntry<"blog">[], tag: string) =>
  await getSortedPosts(
    posts.filter(post => slugifyAll(post.data.tags).includes(tag))
  );

export default getPostsByTag;

In src/pages/tags/[tag]/index.astro the getPostsByTag function needs to be updated to have await.