Skip to content

Commit

Permalink
Update dependency list to display information about new versions
Browse files Browse the repository at this point in the history
When available, dependencies in the list will be colour-coded using
Tailwind colour names:
- `sky` for up-to-date dependencies
- `amber` for dependencies with minor version updates
- `orange` for dependencies with major version updates
- `red` for dependencies that are no longer supported
- `stone` for dependencies for which no information is available

The capsules containing the dependency information will use these
colours.

Additionally, for out-of-date dependencies, the latest version is
displayed in a separate, prominently coloured capsule, with an icon as a
secondary indicator of the outdatedness of the dependency. These icons
are class names for [Boxicons](https://boxicons.com/) icons:
- `bx-up-arrow-circle` for minor updates
- `bxs-up-arrow-circle` for major updates
- `bxs-x-circle` for end-of-life
  • Loading branch information
danlivings-dxw committed Sep 16, 2024
1 parent 322af04 commit fba54ac
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 8 deletions.
9 changes: 7 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@ nunjucks.configure({

const httpServer = createServer(async (request, response) => {
const pathToRepos = "./data/repos.json";
const persistedData = await readFromJsonFile(pathToRepos);
const pathToLifetimes = "./data/lifetimes.json";

const [ persistedRepoData, persistedLifetimeData ] = await Promise.all([
readFromJsonFile(pathToRepos),
readFromJsonFile(pathToLifetimes),
]);

const template = nunjucks.render(
"index.njk",
mapRepoFromStorageToUi(persistedData)
mapRepoFromStorageToUi(persistedRepoData, persistedLifetimeData)
);

return response.end(template);
Expand Down
21 changes: 18 additions & 3 deletions index.njk
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<title>Towtruck</title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href='https://unpkg.com/[email protected]/css/boxicons.min.css' rel='stylesheet'>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-stone-100 text-stone-800">
Expand Down Expand Up @@ -68,9 +69,23 @@
<ul class="space-y-2 hidden group-has-[:checked]:block">
{% for dependency in repo.dependencies %}
<li>
<div class="inline-block text-[0px]">
<span class="font-mono text-sm bg-stone-200 rounded-l-full py-1 px-2">{{ dependency.name }}</span>
<span class="font-mono text-sm bg-stone-300 rounded-r-full py-1 px-2">{{ dependency.version }}</span>
<div class="inline-block whitespace-nowrap text-[0px]">
<span>
<span class="font-mono text-sm bg-{{ dependency.color }}-200 rounded-l-full last:rounded-r-full py-1 px-2">{{ dependency.name }}</span>
{% if dependency.version %}
<span class="font-mono text-sm bg-{{ dependency.color }}-300 last:rounded-r-full py-1 px-2">v{{ dependency.version }}</span>
{% endif %}
{% if dependency.tag %}
<span class="font-mono text-sm bg-{{ dependency.color }}-400 text-white last:rounded-r-full py-1 px-2">{{ dependency.tag }}</span>
{% endif %}
</span>
{% if dependency.isOutdated %}
<span class="inline-block w-1"></span>
<span class="font-mono text-sm bg-{{ dependency.color }}-500 text-white rounded-full py-1 px-2">
<i class="align-[-1px] bx {{dependency.icon}}"></i>
v{{ dependency.latestVersion }}
</span>
{% endif%}
</div>
</li>
{% endfor %}
Expand Down
80 changes: 77 additions & 3 deletions utils/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { readFile } from "fs/promises";
import { getDependencyState } from "./endOfLifeDateApi/index.js";

/**
* @typedef {Object} PersistedData
Expand All @@ -12,17 +13,90 @@ import { readFile } from "fs/promises";
* @property {UiRepo[]} repos - An array of repos.
*/

/**
* @typedef {Object} UiDependency
* @property {string} name
* @property {string?} version
* @property {string?} tag
* @property {string} color
*/

/**
* Maps the persisted dependency data from storage to a format suitable for the UI
* @param {import("../model/Dependency").Dependency} dependency
* @param {import("./endOfLifeDateApi/fetchAllDependencyEolInfo").DependencyLifetimes[]} persistedLifetimes
* @returns {UiDependency}
*/
export const mapDependencyFromStorageToUi = (dependency, persistedLifetimes) => {
const lifetimes = persistedLifetimes.lifetimes.find((item) => item.dependency === dependency.name);

const state = lifetimes === undefined ? "unknown" : getDependencyState(dependency, lifetimes.lifetimes);
const latestVersion = lifetimes?.lifetimes[0]?.latest;

let color;
switch (state) {
case "unknown":
color = "stone";
break;
case "upToDate":
color = "sky";
break;
case "minorUpdateAvailable":
color = "amber";
break;
case "majorUpdateAvailable":
color = "orange";
break;
case "endOfLife":
color = "red";
break;
}

let icon;
switch (state) {
case "unknown":
icon = "bx-question-mark";
break;
case "upToDate":
icon = "bx-check-circle";
break;
case "minorUpdateAvailable":
icon = "bx-up-arrow-circle";
break;
case "majorUpdateAvailable":
icon = "bxs-up-arrow-circle";
break;
case "endOfLife":
icon = "bxs-x-circle";
break;
}

return {
name: dependency.name,
version: dependency.version,
tag: dependency.tag,
color,
icon,
isOutdated: state !== "upToDate" && state !== "unknown",
latestVersion,
}
}

/**
* Maps the persisted repo data from storage to a format suitable for the UI
* @param {PersistedData} persistedData
* @param {import("./endOfLifeDateApi/fetchAllDependencyEolInfo").DependencyLifetimes[]} persistedLifetimes
* @returns {RepoData}
*/
export const mapRepoFromStorageToUi = (persistedData) => {
export const mapRepoFromStorageToUi = (persistedData, persistedLifetimes) => {
const mappedRepos = persistedData.repos.map((repo) => {
const newDate = new Date(repo.updatedAt).toLocaleDateString();
const dependencies = repo.dependencies.map((dependency) => mapDependencyFromStorageToUi(dependency, persistedLifetimes));

return {
...repo,
updatedAt: newDate,
dependencies,
};
});

Expand Down Expand Up @@ -54,7 +128,7 @@ export const readFromJsonFile = async (filePath) => {
* @property {string} language - The primary language the repo is written in.
* @property {string[]} topics - An array of topics associated with the repo - conventially in dxw this is used to list the owners of the repo (EG govpress, delivery-plus).
* @property {number} open_issues - The number of open issues on the repo.
* @property {string[]} dependencies - An array of dependencies used by the repo.
* @property {import("../model/Dependency").Dependency[]} dependencies - An array of dependencies used by the repo.
*/

/**
Expand All @@ -69,7 +143,7 @@ export const readFromJsonFile = async (filePath) => {
* @property {string} language - The primary language the repo is written in.
* @property {string[]} topics - An array of topics associated with the repo - conventially in dxw this is used to list the owners of the repo (EG govpress, delivery-plus).
* @property {number} openIssues - The number of open issues on the repo.
* @property {string[]} dependencies - An array of dependencies used by the repo.
* @property {import("../model/Dependency").Dependency[]} dependencies - An array of dependencies used by the repo.
*/

/**
Expand Down
5 changes: 5 additions & 0 deletions utils/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ describe("mapRepoFromStorageToUi", () => {
language: null,
topics: [],
openIssues: 0,
dependencies: [],
},
];

Expand All @@ -35,6 +36,7 @@ describe("mapRepoFromStorageToUi", () => {
language: null,
topics: [],
openIssues: 0,
dependencies: [],
},
];

Expand All @@ -54,6 +56,7 @@ describe("mapRepoFromStorageToUi", () => {
language: null,
topics: [],
openIssues: 0,
dependencies: [],
},
{
name: "repo2",
Expand All @@ -66,6 +69,7 @@ describe("mapRepoFromStorageToUi", () => {
language: null,
topics: [],
openIssues: 0,
dependencies: [],
},
{
name: "repo3",
Expand All @@ -78,6 +82,7 @@ describe("mapRepoFromStorageToUi", () => {
language: null,
topics: [],
openIssues: 0,
dependencies: [],
},
];
const persistedData = {
Expand Down

0 comments on commit fba54ac

Please sign in to comment.