Skip to content

Commit 6deaa4b

Browse files
committed
Reserve Article URLs for legacy routes
1 parent 4e72fcd commit 6deaa4b

5 files changed

Lines changed: 10 additions & 13 deletions

File tree

.github/workflows/add-article.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,13 @@ jobs:
7272
if not slug:
7373
# Title was only punctuation / non-ASCII — fall back to the issue number.
7474
slug = f"article-{os.environ['ISSUE_NUMBER']}"
75-
route = f"/articles/{today.strftime('%Y-%m-%d')}-{slug}/"
7675
7776
fm = {
7877
'title': title,
7978
'author': author,
8079
'authors': [author] if author else [],
8180
'date': date_str,
8281
'description': description,
83-
'url': route,
8482
}
8583
if category:
8684
fm['categories'] = [category]

CONTRIBUTING.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ If you're not comfortable with Git, you can pitch or submit your article through
3232
authors:
3333
- Your Name
3434
date: "YYYY-MM-DDT00:00:00+00:00"
35-
url: /articles/YYYY-MM-DD-your-article-slug/
3635
categories:
3736
- Category Name
3837
tags:
@@ -41,8 +40,11 @@ If you're not comfortable with Git, you can pitch or submit your article through
4140
---
4241

4342
Your article content in Markdown goes here.
43+
4444
```
4545

46+
The Article URL is derived from this hierarchy as `/articles/YYYY/MM/your-article-slug/`. Do not add `url:` for a new Article; it is reserved for migrated Articles that preserve a historic dated URL.
47+
4648
> Tip: create a new Article with Hugo from the repository root. Set `$slug` to a lowercase, hyphenated title, then replace the generated front matter values:
4749
>
4850
> ```powershell

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ PowerShell.org stewards.
1010
You're in the right place — most contributions don't require running the site at
1111
all. **[`CONTRIBUTING.md`](CONTRIBUTING.md)** walks you through each one:
1212

13-
- **Write a guest blog post** — submit a Markdown article via a GitHub issue form
14-
or a pull request to `content/articles/`.
15-
- **Add your author profile** — an avatar, bio, and links on your `/authors/<you>/`
16-
page. Profiles are opt-in; your byline works without one.
13+
- **Write a guest blog post** — submit a Markdown Article via a GitHub issue form or a pull request. Articles are organized in `content/articles/` by publication year and month.
14+
- **Add your Author Profile** — an avatar, bio, and links on your `/authors/<you>/` page. Profiles are opt-in; your byline works without one.
1715
- **Submit a community event** — get your PowerShell event onto the calendar.
1816
- **Report a bug or broken link** — open an issue.
1917

archetypes/articles.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ author: ""
55
authors:
66
- ""
77
date: '{{ .Date }}'
8-
url: '/articles/{{ .Date.Format "2006-01-02" }}-{{ .File.ContentBaseName }}/'
98
categories: []
109
tags: []
1110
draft: true

scripts/validate-article-bundles.mjs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,19 @@ for (const path of walk(contentRoot).filter((path) => path.endsWith(`${sep}index
5050
if (parts.length !== 4 || !/^\d{4}$/.test(parts[0]) || !/^\d{2}$/.test(parts[1])) continue;
5151
const markdown = readFileSync(path, 'utf8');
5252
const metadata = frontMatter(markdown);
53-
const route = stringValue(metadata, 'url');
54-
if (!route) fail(`Article must declare its preserved dated URL: ${path}`);
55-
else sourceArticles.set(route, { aliases: listValues(metadata, 'aliases'), authors: listValues(metadata, 'authors'), categories: listValues(metadata, 'categories'), tags: listValues(metadata, 'tags'), year: parts[0], month: parts[1], path });
53+
const explicitRoute = stringValue(metadata, 'url');
54+
const route = explicitRoute ?? `/articles/${parts[0]}/${parts[1]}/${parts[2]}/`;
55+
sourceArticles.set(route, { aliases: listValues(metadata, 'aliases'), authors: listValues(metadata, 'authors'), categories: listValues(metadata, 'categories'), tags: listValues(metadata, 'tags'), explicitRoute, year: parts[0], month: parts[1], path });
5656
for (const asset of markdown.matchAll(/\]\((\/images\/articles\/[^)#?]+)/g)) {
5757
if (!existsSync(join(outputRoot, asset[1].replace(/^\//, '')))) fail(`Referenced static asset is missing: ${asset[1]} (${path})`);
5858
}
5959
}
6060

6161
const inventoryByRoute = new Map(inventory.map((article) => [article.route, article]));
62-
for (const route of sourceArticles.keys()) if (!inventoryByRoute.has(route)) fail(`Unexpected Article route: ${route}`);
62+
for (const [route, source] of sourceArticles) if (source.explicitRoute && !inventoryByRoute.has(route)) fail(`Unexpected preserved Article route: ${route}`);
6363
for (const article of inventory) {
6464
const source = sourceArticles.get(article.route);
65-
if (!source) { fail(`Missing migrated Article for preserved route: ${article.route}`); continue; }
65+
if (!source || source.explicitRoute !== article.route) { fail(`Missing migrated Article for preserved route: ${article.route}`); continue; }
6666
if (article.aliases.some((alias) => !source.aliases.includes(alias))) fail(`Article aliases changed: ${article.route}`);
6767
for (const field of ['authors', 'categories', 'tags']) if (JSON.stringify(source[field]) !== JSON.stringify(article[field])) fail(`Article ${field} changed: ${article.route}`);
6868
if (article.draft) continue;

0 commit comments

Comments
 (0)