Automatically update your GitHub readme through GitHub Actions

Pawel Grzybek has set up a workflow that uses GitHub Actions’ Scheduled Events to automatically update his profile It runs every 6 hours and pulls in his RSS feed to populate the

import fs from "fs";
import fetch from "node-fetch";
import parser from "xml2json";

const FEED_URL = "";
const TAG_OPEN = `<!-- FEED-START -->`;
const TAG_CLOSE = `<!-- FEED-END -->`;

const fetchArticles = async () => {
  const articles = await fetch(FEED_URL);
  const articlesText = await articles.text();
  const articlesJSON = parser.toJson(articlesText);
  const newC = JSON.parse(articlesJSON), 5);

  return{ title, link }) => `- [${title}](${link})`).join("\n");

async function main() {
  const readme = fs.readFileSync("./", "utf8");
  const indexBefore = readme.indexOf(TAG_OPEN) + TAG_OPEN.length;
  const indexAfter = readme.indexOf(TAG_CLOSE);
  const readmeContentChunkBreakBefore = readme.substring(0, indexBefore);
  const readmeContentChunkBreakAfter = readme.substring(indexAfter);

  const posts = await fetchArticles();

  const readmeNew = `

  fs.writeFileSync("./", readmeNew.trim());

try {
} catch (error) {

Fetch most recent posts to your GitHub profile page using GitHub workflow and Node.js →

💡 People have been doing lots of nice stuff with their README’s. There’s a curated list to be found at matiassingers/awesome-readme (and plenty of others if you search for them)

Building with Friction

Tim Kaldec takes a look at our modern workflow – in which lots of tools have removed friction – and makes the case to add some “healthy friction”:

A lot of modern workflow improvements have been around removing friction. We want to make it easier to deploy rapidly. Tools like npm make it very easy to gain access to any and all modules we could think of. Tag management enables folks to very quickly add another third-party service.

All of these things, on the surface, provide some value, but the consequences are tremendous. Because these processes remove friction, they don’t ever really give us a chance to pause and consider what we’re doing.

Re-introducing some healthy friction, some moments of pause, in our processes is critical to ensuring a higher level of quality overall.

Examples would be installers that prevent you from installing large bundles, or build pipelines that mark the build as failed when the total bundle size exceeds a certain goal.

Tim Kaldec →

Puppeteer 2.1.0, with native Firefox support

Late January Puppeteer 2.1.0 got released, with native support for Firefox:

The launcher now has an option to run Puppeteer with different browsers, starting with Firefox. Puppeteer can now talk to a real, unpatched Firefox binary. This is a first step towards eventually deprecating the separate puppeteer-firefox package in favor of supporting Firefox directly in puppeteer itself.

You can define which browser to use by setting product on the options object to either "chrome" or "firefox", or by setting the PUPPETEER_PRODUCT env variable to one of those values.

puppeteer.launch({product: 'firefox'});

Installation still per NPM/Yarn

npm i puppeteer

Puppeteer →

Wombat Dressing Room, an npm publication proxy on GCP

When automating the publishing of an NPM package, 2FA can get in the way, as you can’t really automate entering a 2FA auth code off a cellphone. Enter Wombat Dressing Room from Google:

With Wombat Dressing Room, rather than an individual configuring two factor authentication in an authenticator app, 2FA is managed by a shared proxy server..

  • You publish to Wombat Dressing Room, and it enforces additional security rules, before redirecting to
  • Publishes are made from a single npm account with 2FA enabled (a bot account).
  • Publishes can be made using the npm CLI, by making Wombat Dressing Room the default registry (npm config set registry

The Wombat Dressing Room is deployed to Google App Engine. They’ve been using it themselves internally for over a year, in case you were wondering if it is “production ready”.

Wombat Dressing Room Introductory Post →
Wombat Dressing Room Proxy Source (GitHub) →

Programmatically add scripts to package.json with npm-add-script

Recently I needed to automate the addition of the addition of a script defined in a package.json‘s scripts section. To do this I used npm-add-script (an older, but still functioning project), along with the aforementioned npx.

For example, to add a script labelled start with the contents webpack-dev-server --config ./config/webpack.config.babel.js --env.MODE=development --open --hot, I use:

npx npm-add-script \
  -k "start" \
  -v "webpack-dev-server --config ./config/webpack.config.babel.js --env.MODE=development --open --hot" \

Using the --force I enforce overwriting of any existing start script.

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Automatically set up your Mac and configure macOS using ./freshinstall

Recently I configured my new MacBook Pro. I decided to start with a clean slate and not migrate anything from my old MacBook. To configure macOS I whipped up ./freshinstall, which automates that process.

Steps included are:

  1. Configure macOS Preferences and the like
  2. Generate and load SSH keys
  3. Install the essentials: XCode, Git, Homebrew
  4. Copy over my (starter) dotfiles (also contained in the repo)
  5. Install (and sometimes also configure) all most of the software that I use.

The heavy lifting is done by defaults and Homebrew + Homebrew-Cask. Thanks to the aforementioned mas-cli, I’m also able to automatically install software from the Mac App Store.

Many of the settings are inspired upon the work of others. Mainly Mathias Bynens his dotfiles repo came in handy, along with a few other repos and of course the use of a few Google Search Coupons.


Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)

Puppeteer – Headless Chrome Node API

Puppeteer is a Node library which provides a high-level API to control headless Chrome over the DevTools Protocol. It can also be configured to use full (non-headless) Chrome.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('');
  await page.screenshot({path: 'example.png'});


Puppeteer – Headless Chrome Node API →

Chromeless Playground: Chrome Automation Made Simple

With Chrome 59 came the ability to run a Headless Chrome. Controlling it via code isn’t that easy nor elegant. Enter Chromeless (not be confused with Mozilla’s Chromeless):

With Chromeless you can control Chrome (open website, click elements, fill out forms…) using an elegant API. This is useful for integration tests or any other scenario where you’d need to script a real browser.

Runs locally or headless on AWS Lambda. The API to control it is really elegant, as the code is very easy to understand:

const { Chromeless } = require('chromeless');

async function run() {
  const chromeless = new Chromeless();

  const screenshot = await chromeless
    .type('chromeless', 'input[name="q"]')

  console.log(screenshot); // prints local file path or S3 url

  await chromeless.end();


Chromeless Playground →
graphcool/chromeless GitHub →

Shentong’s Package Sorting Army of Robots

Orange robots at the company’s sorting stations are able to identify the destination of a package through a code-scan, virtually eliminating sorting mistakes. Shentong’s army of robots can sort up to 200,000 packages a day, and are self-charging, meaning they are operational 24/7.