Skip to content

Commit 336e427

Browse files
authored
infra: remove dead Hashnode cross-post channel (#20)
Hashnode has been failing every cross-post run for the past month (seen on 2026-05-12 and again on 2026-05-18, both with the same "Unexpected token '<', '<!DOCTYPE'..." symptom indicating the API now returns HTML where it used to return JSON). The platform itself appears to be defunct, so rather than chase a moving API surface, this removes the integration entirely. Removed: - scripts/channels/hashnode.mjs (the channel module) - scripts/__tests__/hashnode.test.mjs (5 tests, all green prior) - Hashnode entries in the cross-post.yml and update-cross-post.yml workflow env blocks (HASHNODE_TOKEN, HASHNODE_PUBLICATION_ID) - Hashnode imports + ALL_CHANNELS entries in scripts/cross-post.mjs and scripts/update-cross-post.mjs - Mentions of Hashnode in usage docstrings and the update-cross-post channels description Remaining cross-post surface: dev.to (post + update) and Bluesky (post only). Both still passing the unit suite (32 tests, was 37). Build clean, 17 pages. The HASHNODE_TOKEN and HASHNODE_PUBLICATION_ID GitHub Secrets are left in place since removing them via the API is out of scope for this PR; harmless once nothing references them.
1 parent 3724895 commit 336e427

6 files changed

Lines changed: 7 additions & 207 deletions

File tree

.github/workflows/cross-post.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ jobs:
3636
- name: Cross-post
3737
env:
3838
DEV_TO_API_KEY: ${{ secrets.DEV_TO_API_KEY }}
39-
HASHNODE_TOKEN: ${{ secrets.HASHNODE_TOKEN }}
40-
HASHNODE_PUBLICATION_ID: ${{ secrets.HASHNODE_PUBLICATION_ID }}
4139
BSKY_HANDLE: ${{ secrets.BSKY_HANDLE }}
4240
BSKY_APP_PASSWORD: ${{ secrets.BSKY_APP_PASSWORD }}
4341
run: |

.github/workflows/update-cross-post.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
description: 'Slug to re-push (uses current source markdown)'
88
required: true
99
channels:
10-
description: 'Comma-separated channel filter (e.g. devto,hashnode). Empty = all updatable channels.'
10+
description: 'Comma-separated channel filter (e.g. devto). Empty = all updatable channels.'
1111
required: false
1212
dry_run:
1313
description: 'Dry run (look up posts, do not update)'
@@ -30,8 +30,6 @@ jobs:
3030
- name: Update cross-posts
3131
env:
3232
DEV_TO_API_KEY: ${{ secrets.DEV_TO_API_KEY }}
33-
HASHNODE_TOKEN: ${{ secrets.HASHNODE_TOKEN }}
34-
HASHNODE_PUBLICATION_ID: ${{ secrets.HASHNODE_PUBLICATION_ID }}
3533
run: |
3634
ARGS="--slug=${{ github.event.inputs.slug }}"
3735
if [ -n "${{ github.event.inputs.channels }}" ]; then

scripts/__tests__/hashnode.test.mjs

Lines changed: 0 additions & 25 deletions
This file was deleted.

scripts/channels/hashnode.mjs

Lines changed: 0 additions & 168 deletions
This file was deleted.

scripts/cross-post.mjs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@
1414

1515
import * as bluesky from './channels/bluesky.mjs';
1616
import * as devto from './channels/devto.mjs';
17-
import * as hashnode from './channels/hashnode.mjs';
1817
import { detectNewlyPublished, loadArticle, resolvePosts } from './lib/post-source.mjs';
1918

20-
const ALL_CHANNELS = [devto, hashnode, bluesky];
19+
const ALL_CHANNELS = [devto, bluesky];
2120

2221
function parseArgs(argv) {
2322
const out = { dryRun: false, slug: null, channels: null };

scripts/update-cross-post.mjs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
#!/usr/bin/env node
22
// Re-publish the current source markdown to existing cross-posts.
33
// Use this when an already-cross-posted article gets a substantive edit
4-
// that needs to propagate to dev.to, Hashnode, etc. The companion
5-
// cross-post script is intentionally skip-if-exists, so edits do not
6-
// flow through it.
4+
// that needs to propagate to dev.to. The companion cross-post script is
5+
// intentionally skip-if-exists, so edits do not flow through it.
76
//
87
// Usage:
98
// node scripts/update-cross-post.mjs --slug=foo
@@ -12,14 +11,13 @@
1211
// node scripts/update-cross-post.mjs --slug=foo --channels=devto
1312
//
1413
// Channels are auto-discovered. Only channels that export an `update`
15-
// function are eligible (Bluesky is excluded editing a feed item in
14+
// function are eligible (Bluesky is excluded - editing a feed item in
1615
// place is rude / unsupported).
1716

1817
import * as devto from './channels/devto.mjs';
19-
import * as hashnode from './channels/hashnode.mjs';
2018
import { loadArticle, resolvePosts } from './lib/post-source.mjs';
2119

22-
const ALL_CHANNELS = [devto, hashnode];
20+
const ALL_CHANNELS = [devto];
2321

2422
function parseArgs(argv) {
2523
const out = { slugs: [], dryRun: false, channels: null };
@@ -86,7 +84,7 @@ export async function processArticle(article, channels, dryRun) {
8684
async function main() {
8785
const args = parseArgs(process.argv.slice(2));
8886
if (args.slugs.length === 0) {
89-
console.error('Usage: node scripts/update-cross-post.mjs --slug=<slug> [--slug=<slug>...] [--channels=devto,hashnode] [--dry-run]');
87+
console.error('Usage: node scripts/update-cross-post.mjs --slug=<slug> [--slug=<slug>...] [--channels=devto] [--dry-run]');
9088
process.exit(1);
9189
}
9290

0 commit comments

Comments
 (0)