Skip to content

Commit 5436e4f

Browse files
authored
fix: Add index.html redirect, historical version aliases, timestamp placeholder, and bump to 1.3.1 (#18)
1 parent d944ca7 commit 5436e4f

6 files changed

Lines changed: 117 additions & 20 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,75 @@ jobs:
8888
# Copy base file
8989
cp tokens/token-list.json dist/token-list.json
9090
91-
# Copy all historical versions
91+
# Copy all historical versions and create major.minor and major aliases
9292
if [ -d "versions" ]; then
93-
cp versions/*.json dist/ 2>/dev/null || true
94-
echo "Copied historical versions from versions/ directory"
93+
# Declare associative arrays to track highest versions
94+
declare -A highest_patch # tracks highest patch for each major.minor
95+
declare -A highest_minor # tracks highest minor.patch for each major
96+
97+
for version_file in versions/*.json; do
98+
if [ -f "$version_file" ]; then
99+
# Copy the full version file (e.g., v1.2.0.json)
100+
cp "$version_file" dist/
101+
102+
# Extract version components from the file content
103+
FILE_MAJOR=$(jq -r '.version.major' "$version_file")
104+
FILE_MINOR=$(jq -r '.version.minor' "$version_file")
105+
FILE_PATCH=$(jq -r '.version.patch' "$version_file")
106+
107+
# Skip if this is the current version's major (already created above)
108+
if [ "$FILE_MAJOR" = "$MAJOR" ]; then
109+
echo "Skipping major alias v${FILE_MAJOR}.json - current version takes precedence"
110+
# But still process minor alias if different minor version
111+
if [ "$FILE_MINOR" = "$MINOR" ]; then
112+
echo "Skipping minor alias v${FILE_MAJOR}.${FILE_MINOR}.json - current version takes precedence"
113+
continue
114+
fi
115+
fi
116+
117+
# Create/update major.minor alias (e.g., v1.2.json)
118+
MINOR_KEY="${FILE_MAJOR}.${FILE_MINOR}"
119+
MINOR_ALIAS="v${MINOR_KEY}.json"
120+
CURRENT_PATCH=${highest_patch[$MINOR_KEY]:-"-1"}
121+
if [ "$FILE_PATCH" -gt "$CURRENT_PATCH" ]; then
122+
cp "$version_file" "dist/${MINOR_ALIAS}"
123+
highest_patch[$MINOR_KEY]=$FILE_PATCH
124+
echo "Created/updated alias ${MINOR_ALIAS} from $(basename $version_file)"
125+
fi
126+
127+
# Create/update major alias (e.g., v1.json) - only for non-current majors
128+
if [ "$FILE_MAJOR" != "$MAJOR" ]; then
129+
MAJOR_ALIAS="v${FILE_MAJOR}.json"
130+
# Compare as "minor.patch" to find highest version within major
131+
CURRENT_MINOR_PATCH=${highest_minor[$FILE_MAJOR]:-"-1.-1"}
132+
CURRENT_M=$(echo $CURRENT_MINOR_PATCH | cut -d. -f1)
133+
CURRENT_P=$(echo $CURRENT_MINOR_PATCH | cut -d. -f2)
134+
if [ "$FILE_MINOR" -gt "$CURRENT_M" ] || ([ "$FILE_MINOR" = "$CURRENT_M" ] && [ "$FILE_PATCH" -gt "$CURRENT_P" ]); then
135+
cp "$version_file" "dist/${MAJOR_ALIAS}"
136+
highest_minor[$FILE_MAJOR]="${FILE_MINOR}.${FILE_PATCH}"
137+
echo "Created/updated alias ${MAJOR_ALIAS} from $(basename $version_file)"
138+
fi
139+
fi
140+
fi
141+
done
142+
echo "Copied historical versions and created aliases"
95143
fi
96144
145+
# Create index.html that redirects to latest.json
146+
cat > dist/index.html << 'EOF'
147+
<!DOCTYPE html>
148+
<html lang="en">
149+
<head>
150+
<meta charset="utf-8">
151+
<title>Request Network Token List</title>
152+
<meta http-equiv="refresh" content="0; url=latest.json">
153+
</head>
154+
<body>
155+
<p>Redirecting to <a href="latest.json">latest.json</a>...</p>
156+
</body>
157+
</html>
158+
EOF
159+
97160
echo "Created versioned files: v${VERSION}.json, v${MAJOR}.${MINOR}.json, v${MAJOR}.json, latest.json"
98161
99162
- name: Deploy to GitHub Pages

src/schemas/token-list-schema.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
},
1010
"timestamp": {
1111
"type": "string",
12-
"format": "date-time",
13-
"description": "The timestamp of when this list was last updated"
12+
"description": "The timestamp of when this list was last updated (ISO 8601 format)",
13+
"oneOf": [
14+
{ "format": "date-time" },
15+
{ "const": "Set automatically during deployment" }
16+
]
1417
},
1518
"version": {
1619
"type": "object",

src/types/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ export enum NetworkType {
5454
FIAT = "fiat",
5555
}
5656

57+
/**
58+
* Placeholder value for timestamp in the source token list file.
59+
* The actual timestamp is set during deployment by the GitHub Actions workflow.
60+
*/
61+
export const TIMESTAMP_PLACEHOLDER = "Set automatically during deployment";
62+
5763
export const CHAIN_IDS: Record<string, number> = {
5864
mainnet: 1,
5965
matic: 137,

src/validation/validate.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
NetworkType,
99
TokenType,
1010
CHAIN_IDS,
11+
TIMESTAMP_PLACEHOLDER,
1112
} from "../types";
1213
import schema from "../schemas/token-list-schema.json";
1314

@@ -112,9 +113,22 @@ function isValidVersion(version: {
112113
);
113114
}
114115

116+
/**
117+
* Validates the timestamp field.
118+
*
119+
* Note: The JSON schema allows both date-time format and the placeholder string
120+
* so that consumers can use the schema to validate deployed token lists.
121+
* However, THIS validation script enforces the placeholder because it runs
122+
* on the source file (tokens/token-list.json) during CI. The actual timestamp
123+
* is set during deployment by the GitHub Actions workflow.
124+
*/
115125
function isValidTimestamp(timestamp: string): boolean {
116-
const date = new Date(timestamp);
117-
return date.toString() !== "Invalid Date";
126+
// Source file must use placeholder - actual timestamp is set during deployment
127+
if (timestamp !== TIMESTAMP_PLACEHOLDER) {
128+
console.error(`Timestamp must be '${TIMESTAMP_PLACEHOLDER}' - actual timestamp is set during deployment`);
129+
return false;
130+
}
131+
return true;
118132
}
119133

120134
function isValidDecimals(decimals: number): boolean {

tests/validation.test.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, it, expect } from "vitest";
22
import { validateTokenList } from "../src/validation/validate";
3-
import { NetworkType, TokenList, TokenType, CHAIN_IDS } from "../src/types";
3+
import { NetworkType, TokenList, TokenType, CHAIN_IDS, TIMESTAMP_PLACEHOLDER } from "../src/types";
44

55
describe("Token List Validation", () => {
66
const validToken = {
@@ -17,7 +17,7 @@ describe("Token List Validation", () => {
1717
it("should validate a correct token list", async () => {
1818
const validList: TokenList = {
1919
name: "Test Token List",
20-
timestamp: new Date().toISOString(),
20+
timestamp: TIMESTAMP_PLACEHOLDER,
2121
version: { major: 1, minor: 0, patch: 0 },
2222
tokens: [validToken],
2323
};
@@ -28,7 +28,7 @@ describe("Token List Validation", () => {
2828
it("should reject invalid token addresses", async () => {
2929
const invalidList: TokenList = {
3030
name: "Test Token List",
31-
timestamp: new Date().toISOString(),
31+
timestamp: TIMESTAMP_PLACEHOLDER,
3232
version: { major: 1, minor: 0, patch: 0 },
3333
tokens: [
3434
{
@@ -44,7 +44,7 @@ describe("Token List Validation", () => {
4444
it("should reject duplicate token IDs", async () => {
4545
const duplicateList: TokenList = {
4646
name: "Test Token List",
47-
timestamp: new Date().toISOString(),
47+
timestamp: TIMESTAMP_PLACEHOLDER,
4848
version: { major: 1, minor: 0, patch: 0 },
4949
tokens: [
5050
validToken,
@@ -61,7 +61,7 @@ describe("Token List Validation", () => {
6161
it("should reject invalid decimals", async () => {
6262
const invalidList: TokenList = {
6363
name: "Test Token List",
64-
timestamp: new Date().toISOString(),
64+
timestamp: TIMESTAMP_PLACEHOLDER,
6565
version: { major: 1, minor: 0, patch: 0 },
6666
tokens: [
6767
{
@@ -77,7 +77,7 @@ describe("Token List Validation", () => {
7777
it("should validate version format", async () => {
7878
const invalidList: TokenList = {
7979
name: "Test Token List",
80-
timestamp: new Date().toISOString(),
80+
timestamp: TIMESTAMP_PLACEHOLDER,
8181
version: { major: -1, minor: 0, patch: 0 },
8282
tokens: [validToken],
8383
};
@@ -88,7 +88,7 @@ describe("Token List Validation", () => {
8888
it("should validate network type", async () => {
8989
const invalidList: TokenList = {
9090
name: "Test Token List",
91-
timestamp: new Date().toISOString(),
91+
timestamp: TIMESTAMP_PLACEHOLDER,
9292
version: { major: 1, minor: 0, patch: 0 },
9393
tokens: [
9494
{
@@ -104,7 +104,7 @@ describe("Token List Validation", () => {
104104
it("should validate token type", async () => {
105105
const invalidList: TokenList = {
106106
name: "Test Token List",
107-
timestamp: new Date().toISOString(),
107+
timestamp: TIMESTAMP_PLACEHOLDER,
108108
version: { major: 1, minor: 0, patch: 0 },
109109
tokens: [
110110
{
@@ -117,7 +117,18 @@ describe("Token List Validation", () => {
117117
expect(await validateTokenList(invalidList)).toBe(false);
118118
});
119119

120-
it("should validate timestamp format", async () => {
120+
it("should reject real timestamps (must use placeholder)", async () => {
121+
const invalidList: TokenList = {
122+
name: "Test Token List",
123+
timestamp: new Date().toISOString(),
124+
version: { major: 1, minor: 0, patch: 0 },
125+
tokens: [validToken],
126+
};
127+
128+
expect(await validateTokenList(invalidList)).toBe(false);
129+
});
130+
131+
it("should reject invalid timestamp format", async () => {
121132
const invalidList: TokenList = {
122133
name: "Test Token List",
123134
timestamp: "invalid-date",
@@ -131,7 +142,7 @@ describe("Token List Validation", () => {
131142
it("should reject invalid chainId for network", async () => {
132143
const invalidList: TokenList = {
133144
name: "Test Token List",
134-
timestamp: new Date().toISOString(),
145+
timestamp: TIMESTAMP_PLACEHOLDER,
135146
version: { major: 1, minor: 0, patch: 0 },
136147
tokens: [
137148
{
@@ -156,7 +167,7 @@ describe("Token List Validation", () => {
156167
for (const { network, chainId } of networks) {
157168
const validList: TokenList = {
158169
name: "Test Token List",
159-
timestamp: new Date().toISOString(),
170+
timestamp: TIMESTAMP_PLACEHOLDER,
160171
version: { major: 1, minor: 0, patch: 0 },
161172
tokens: [
162173
{

tokens/token-list.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"name": "Request Network Token List",
3-
"timestamp": "2025-12-02T15:43:26.000Z",
3+
"timestamp": "Set automatically during deployment",
44
"version": {
55
"major": 1,
66
"minor": 3,
7-
"patch": 0
7+
"patch": 1
88
},
99
"tokens": [
1010
{

0 commit comments

Comments
 (0)