visit
Wellington
18.4°C
Clouds: 75%
Humidity: 77%
Wind: 8.75 m/s
Pressure: 1001hpa
More..
The more you can automate in your day to day life, the more time you have to spend on what is meaningful. My boss once told me that I am the laziest worker he had ever seen, but he said he meant that as the highest form of praise to describe how efficiently I worked. Or perhaps, he has just trying to be nice.
The repository relies on four main files:We use
nodejs
to run our action on a virtual server provided by GitHub. For this script to work, we need to create a node project:Install the necessary packages:npm i @vercel/ncc node-fetch fs @actions/core
Then append our
packages.json
to include the following script:"scripts": {
"build": "ncc build src/action.js -o dist",
...
},
Before pushing this repository to GitHub make sure to include a
.gitignore
file with node_modules
in at the root level of your directory:node_modules
This ignores the very large and unused directory
node_modules
, which stores the code for all the libraries we just imported. It is not needed by our GitHub action to run.# Weather Forecast
![Weather](//github.com/woodRock/expert-chainsaw/workflows/Weather/badge.svg)
Tumeric single-origin coffee taiyaki, literally craft beer enamel pin plaid chia direct trade 90's distillery retro vaporware. PBR&B hexagon blue bottle banh mi mumblecore synth, pitchfork actually vegan church-key bicycle rights iceland occupy street art. Normcore disrupt you probably haven't heard of them, crucifix single-origin coffee cornhole listicle. Distillery iPhone enamel pin direct trade venmo quinoa jianbing four loko bespoke unicorn.
## Forecast
<!-- FEED-START -->
<!-- FEED-END -->
At the top of this file, we have a title,
# Weather Forecast
, this is similar to the <h1>
tag from HTML, where the number of hashes #
denotes the weight of the header (i.e., ##
equals <h2>
, ###
equals <h3>
, etc ... ). Also worth noting, [Hipster Ipsum](//hipsum.co/) can be used to create amusing placeholder text as seen above.We then have an embedded Action's workflow badge. These can be generated by clicking the
Actions
tab on the repository. Selecting a workflow, in our case weather
. Expanding the three dots ...
option in the top right, and clicking create status badge
. This automatically generates the embedded HTML necessary to be added to our Markdown file. Programmers love these badges, and adorn them to their repositories as badges of honour, so welcome to the club!We then have a secondary header for our weather forecast
## Forecast
, followed by an HTML comment. This comment is hidden when viewing the README on the URL, but it is there when we edit the document. Notice the tags FEED-START
and FEED-END
. This let our action.js
script (to be discussed later) know where we want to append our weather forecast. We use these tags so that we do not overwrite the existing content of the README, each time a new forecast is given.name: "Weather Forecast Action"
description: "Display weather forecast for any area in a project README"
author: "woodrock"
inputs:
GITHUB_TOKEN:
description: "GitHub token"
required: true
OPEN_WEATHER_TOKEN:
description: "Open Weather API Token"
required: true
CITY:
description: "City to get the forecast for"
required: true
runs:
using: "node12"
main: "dist/index.js"
GITHUB_TOKEN
OPEN_WEATHER_TOKEN
CITY
These are environment variables that are used by our scripts. The first two provide access to the APIs, so we can generate the necessary content. The
GITHUB_TOKEN
is automatically generated by the repository, so we do not need to worry about that. The
OPEN_WEATHER_TOKEN
is an API key from the . You will need to register an account for this site, should you wish to recreate this action. Once registered, click on your username on the top right of the navigation bar, then select 'My API Keys'. You can either use the default key provided by your account or create an isolated key, to be used specifically by this application. Copy that key into your clipboard.Now we need to create a GitHub secret. This is an environment variable stored securely by GitHub so that we don't expose sensitive information, such as our API Key, to the public. Return to the repository. Click
Settings
. On the navigation bar to the left, select secrets
. Select New Repository Secret
, call it OPEN_WEATHER_TOKEN
and copy and paste your Open Weather API key as its value.We specify the city in the
weather.yml
. This is included so that this action can be reused for different locations. Now, it can be used to forecast the weather anywhere in the world, as opposed to hard-coding a city value in our action.js
. Generic solutions in the software are more maintainable as they encourage re-use without the need to refactor.The script that performs the work is shown below. It is located in the
/src
directory.const fetch = require("node-fetch");
const fs = require("fs");
const core = require("@actions/core");
const FILE_NAME = "./README.md";
const ENCODING = "utf8";
const TAG_OPEN = `<!-- FEED-START -->`;
const TAG_CLOSE = `<!-- FEED-END -->`;
async function fetchWeather() {
const API_KEY = core.getInput("OPEN_WEATHER_TOKEN");
const city = core.getInput("CITY");
const URL = `//api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&mode=html`;
return fetch(URL)
.then((response) => response.text())
.then((html) => html);
}
async function run() {
const readme = fs.readFileSync(FILE_NAME, ENCODING);
const indexBefore = readme.indexOf(TAG_OPEN) + TAG_OPEN.length;
const indexAfter = readme.indexOf(TAG_CLOSE);
const before = readme.substring(0, indexBefore);
const after = readme.substring(indexAfter);
const input = (await fetchWeather()).replace(/<script.*>.*<\/script>/ims, "");
const edited = `
${before}
${input}
${after}`;
fs.writeFileSync(FILE_NAME, edited.trim());
}
try {
run();
} catch (error) {
console.log(error);
}
At the top, we import all the packages we need. We use
fetch
to make an API call using javascript asynchronous functions. fs
allows us to perform FileIO operations, such as reading and writing to a README.md
. @actions/core
allows us to request access to the repository secrets, our OPEN_WEATHER_TOKEN
, and the CITY
environment variable.The
fetchWeather()
method requests the Open Weather API token and performs an API call using fetch()
. We use string templating to structure a custom call to this API:We specify the city and the API key and request that the response from the API in an HTML format so that we can simply embed this into our
README
. The weather API returns an HTML object, that includes <script>
tags to neatly render the response. However, as mentioned previously, we cannot embed JavaScript into a static
README
hosted by GitHub. So instead, we strip the HTML of the <script>
tags and the content inside them. This is done using the replace()
method:await fetchWeather().replace(/<script.*>.*<\/script>/ims, "")
Then we perform some arithmetic, to find where our
FEED-START
and FEED-END
tags are, and append the embedded weather forecast within these tags. Without overwriting any of the existing document outside these tags.This script must be built by running the following command in your terminal:npm run build
This is the GitHub Workflow that runs our script. It is located in the
.github/workflows
directory.name: "Weather"
on:
workflow_dispatch:
schedule:
- cron: "*/30 * * * *" # Runs every 30 minutes
jobs:
update_blogs:
name: "Update Blogs"
runs-on: ubuntu-latest
steps:
- name: "📥 Fetching Repository Contents"
uses: actions/checkout@main
- name: "🌨️ Fetching Weather Forecast"
uses: ./
with:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
OPEN_WEATHER_TOKEN: ${{secrets.OPEN_WEATHER_TOKEN}}
CITY: "Wellington, New Zealand"
- name: "🛠️ Push changes to GitHub"
run:
git config --global user.email [email protected] && git config --global user.name readme-bot;
git diff --quiet && git diff --staged --quiet || git commit -am '[BOT] Update Readme' && git push;
We use the
workflow_dispatch
so we can trigger our script to be run automatically at certain intervals. We specify that interval using . This is a time-based job scheduler from Unix machines. Here */30 * * * *
we set the workflow to be run once every 30 minutes, every hour, every month, every day of the week.Our workflow has three jobs. The first uses
actions/checkout@main
to clone our repository to the virtual machine running the workflow. The second run our script using the node12
docker image we specified in the action.yml
. With the main script being dist/action.js
. We provide access repository secretes, the GITHUB_TOKEN
and OPEN_WEATHER_TOKEN
, under the with
section. We can specify the CITY
of our choice here, in this case, I specify my home city Wellington, New Zealand
.Finally, we push our changes to the repository. The workflow is set to have a GitHub user with the name
readme-bot
. This makes it clear that updates to the README
in our repository are being done by a service account, not an actual user. This is an important distinction, for separating intentional changes by users, from automated ones by our GitHub Action. To reinforce this idea we include [BOT]
in the commit message as well.Previously published at