From 925c85084ecf760b46500eec9c747109904e9400 Mon Sep 17 00:00:00 2001 From: Jeff Merrick Date: Wed, 24 Jun 2026 11:27:56 -0500 Subject: [PATCH 1/3] releases: add monthly updates section with JSON and RSS feeds Restructure the releases list page around a single newest-first `entries` manifest in content/releases/_index.md, where each entry is typed `updates` or `release`. Update months render inline as native
disclosures (title + optional tier badge, expanding to a one-sentence description and an inline Read more link); release entries are pointers whose card visuals and feed items are sourced from the detail page's section cards. Add curated JSON (/releases/index.json) and RSS (/releases/rss.xml) feeds, plus per-item product-tier badges on the updates list and the May release cards. Co-Authored-By: Claude Opus 4.8 --- content/releases/_index.md | 54 ++++++++++++++++++ .../releases/agentic-infrastructure-era.md | 2 + .../partials/releases/card-text-block.html | 5 +- layouts/partials/releases/card.html | 3 +- layouts/partials/releases/updates.html | 44 +++++++++++++++ layouts/releases/list.html | 31 ++++++---- layouts/releases/list.json | 56 +++++++++++++++++++ layouts/releases/rss.xml | 45 +++++++++++++++ 8 files changed, 228 insertions(+), 12 deletions(-) create mode 100644 layouts/partials/releases/updates.html create mode 100644 layouts/releases/list.json create mode 100644 layouts/releases/rss.xml diff --git a/content/releases/_index.md b/content/releases/_index.md index 91cb5bfaa034..ffc41432ce5b 100644 --- a/content/releases/_index.md +++ b/content/releases/_index.md @@ -1,4 +1,58 @@ --- title: Releases meta_desc: A running log of major platform updates from the Pulumi team. + +# Opt this section into JSON and RSS feeds (built in config/_default/config.yml). +# JSON -> /releases/index.json (layouts/releases/list.json) +# RSS -> /releases/rss.xml (layouts/releases/rss.xml) +outputs: + - HTML + - JSON + - RSS + +# A single, newest-first list of release-page entries. Each entry has a `type`: +# - updates: a lightweight month of items, rendered inline on the list page +# as
disclosures. Each item: { title, description, url?, tier? } +# — description is required. +# - release: a pointer to a full release. Everything else — the card's title, +# description, and image, plus the feed's items (sourced from the +# detail page's section cards: title, description, url, tier) — is +# read from the detail page at `url`. Only `date` (for ordering) +# and `url` live here, so there is nothing to hand-sync. +# The list page, JSON (/releases/index.json), and RSS (/releases/rss.xml) all +# render from this array, sorted by `date` descending (newest month first). +entries: + - type: updates + label: June 2026 Updates + date: 2026-06-24 + items: + - title: "Neo code reviews: AI code review built for infrastructure" + description: "Neo reviews each pull request as an agent — reading the code, the preview plan, and the resulting infrastructure diff together — and posts inline findings on the affected lines." + url: /blog/neo-code-reviews/ + tier: Team + - title: "Browse and publish private Terraform modules in the Pulumi Cloud registry" + description: "Publish your existing Terraform and OpenTofu modules to the Pulumi Cloud private registry with the tooling you already use, and browse them in the console — a drop-in migration path from HCP Terraform." + tier: Enterprise + - title: "Dark mode for the docs" + description: "😎 The Pulumi docs now offer a light, dark, and system theme toggle." + url: /docs/ + - title: "Individual user authentication for GitHub Enterprise Server" + description: "Self-hosted GitHub Enterprise operations now run as the individual user who triggered them, so pull requests, commits, and Neo's writes are attributed to that person and respect their permissions." + url: /docs/integrations/version-control/github-app/#individual-user-authentication-for-github-enterprise-server + tier: Business Critical + - title: "One CLI for Pulumi ESC with pulumi env" + description: "The standalone esc CLI is retiring in favor of pulumi env, so a single Pulumi CLI now manages your IaC, Deployments, and ESC environments." + url: /docs/esc/ + - title: "Universal Search: a Cmd/Ctrl+K command palette for Pulumi Cloud" + description: "A keyboard-first command palette (Cmd/Ctrl+K) jumps you to any stack, environment, resource, or member without leaving the page you're on." + - title: "Trigger deployments from Git tags on any supported VCS" + description: "Push a git tag to trigger a deployment, with optional glob filters, across GitHub, GitLab, Bitbucket, Azure DevOps, and Custom VCS." + url: /blog/trigger-deployments-on-git-tags/ + - title: "Pulumi CLI v3.245–v3.248: pulumi logs ls/rm, stack new, richer do and neo" + description: "June's CLI releases add pulumi logs ls/rm, rename stack init to stack new, and bring richer pulumi do and pulumi neo workflows." + url: https://github.com/pulumi/pulumi/releases + + - type: release + date: 2026-05-19 # for ordering; title, label, and feed items come from the detail page + url: /releases/agentic-infrastructure-era/ --- diff --git a/content/releases/agentic-infrastructure-era.md b/content/releases/agentic-infrastructure-era.md index 8081dabd5865..aa90f98cd352 100644 --- a/content/releases/agentic-infrastructure-era.md +++ b/content/releases/agentic-infrastructure-era.md @@ -100,6 +100,7 @@ sections: Now you can @-mention Neo in GitHub issues and pull requests, and in your team's Slack workspace, to kick off full-context infra tasks wherever you are. link: /blog/neo-github-slack/ + tier: Team - variant: text icon: plugs @@ -116,6 +117,7 @@ sections: Use Neo to automate recurring infrastructure tasks like keeping providers updated, identifying non-compliant resources, and summarizing infrastructure changes from week to week. link: /blog/neo-automations/ + tier: Enterprise - anchor: ai-frontier label: Partnering with the frontier of AI infrastructure diff --git a/layouts/partials/releases/card-text-block.html b/layouts/partials/releases/card-text-block.html index 8feb8b6ccbdf..8413579e73b0 100644 --- a/layouts/partials/releases/card-text-block.html +++ b/layouts/partials/releases/card-text-block.html @@ -15,12 +15,15 @@ - title: Card title. - description: Card description (markdown). - isLinked: Bool. Adds the inline arrow icon when true. + - tier: Optional product tier (e.g. "Team"). Renders a badge inline after the + title; the badge picks up a violet tint when a linked card is hovered. */}} {{ $icon := .icon }} {{ $title := .title }} {{ $description := .description }} {{ $isLinked := .isLinked }} +{{ $tier := .tier }}
{{ with $icon }} @@ -29,7 +32,7 @@
{{ end }} {{ with $title }} -

{{ . }}

+

{{ . }}{{ with $tier }}{{ . }}{{ end }}

{{ end }} {{ with $description }}
diff --git a/layouts/partials/releases/card.html b/layouts/partials/releases/card.html index 2cd5e1fa4039..11bd1b401fc8 100644 --- a/layouts/partials/releases/card.html +++ b/layouts/partials/releases/card.html @@ -28,6 +28,7 @@ {{ $image := .image }} {{ $imageAlt := .image_alt | default $title }} {{ $link := .link }} +{{ $tier := .tier }} {{/* Span classes per variant (mobile is always single-column) */}} {{ $spanClass := "lg:col-span-1 lg:row-span-1" }} @@ -50,7 +51,7 @@ {{ end }} {{ end }} -{{ $textBlockArgs := dict "icon" $icon "title" $title "description" $description "isLinked" $isLinked }} +{{ $textBlockArgs := dict "icon" $icon "title" $title "description" $description "isLinked" $isLinked "tier" $tier }} {{ if $isLinked }}{{ else }}
{{ end }} diff --git a/layouts/partials/releases/updates.html b/layouts/partials/releases/updates.html new file mode 100644 index 000000000000..1888dda93478 --- /dev/null +++ b/layouts/partials/releases/updates.html @@ -0,0 +1,44 @@ +{{/* + Releases Updates + + A lightweight, card-less list of one month's update items. A small + uppercase mono overline (the month label) sits above the list. Each item is + a native
disclosure: the row shows the title + optional + tier badge + a chevron, and expanding it reveals a one-sentence description + with an inline "Read more" link when the item has a url. Using as + the toggle keeps the link a plain in-body , so the two never fight over + the click. No JS — matches the no-JS partial convention on /releases. + /releases is not a /docs page, so no dark-mode tokens are needed. + + Parameters: + - label: Overline text (e.g., "June 2026"). + - items: Slice of maps: { title, description, url?, tier? }. description is required. +*/}} + +{{ $label := .label }} +{{ $items := .items }} + +{{ $badgeClasses := "inline-flex items-center align-middle ml-2 rounded-full bg-gray-100 text-gray-600 px-2 py-0.5 text-[10px] font-mono uppercase tracking-wider leading-none" }} + +
+ {{ with $label }} +

{{ . }}

+ {{ end }} + +
+
diff --git a/layouts/releases/list.html b/layouts/releases/list.html index ab5046379775..bf21fad9aca0 100644 --- a/layouts/releases/list.html +++ b/layouts/releases/list.html @@ -9,16 +9,27 @@

{{ .Title }}

- {{ $items := where (where .Site.Pages "Type" "releases") "Kind" "eq" "page" }} - {{ range sort $items ".Params.date" "desc" }} - {{ partial "releases/release-item.html" (dict - "overline" .Params.label - "heading" .Params.title - "description" .Params.short_description - "feature_image" .Params.feature_image - "feature_image_alt" .Params.feature_image_alt - "link" .RelPermalink - ) }} + {{/* One newest-first list of updates + release entries (see _index.md). */}} + {{ range $idx, $entry := sort .Params.entries "date" "desc" }} + {{ if eq $entry.type "updates" }} + {{ partial "releases/updates.html" (dict + "label" $entry.label + "items" $entry.items + "idx" $idx + ) }} + {{ else if eq $entry.type "release" }} + {{/* Card visuals come from the detail page at `url`. */}} + {{ with site.GetPage (strings.TrimSuffix "/" $entry.url) }} + {{ partial "releases/release-item.html" (dict + "overline" .Params.label + "heading" .Params.title + "description" .Params.short_description + "feature_image" .Params.feature_image + "feature_image_alt" .Params.feature_image_alt + "link" .RelPermalink + ) }} + {{ end }} + {{ end }} {{ end }}
diff --git a/layouts/releases/list.json b/layouts/releases/list.json new file mode 100644 index 000000000000..e9d42ce487fb --- /dev/null +++ b/layouts/releases/list.json @@ -0,0 +1,56 @@ +{{- /* + Releases JSON feed -> /releases/index.json + + A single machine-readable document built from the `entries` manifest in + content/releases/_index.md, emitted newest-first by `date`. Each entry + carries a `type`: + - updates: items come from the manifest ({ title, description, url?, tier? }). + - release: a pointer; title/label and the item list are read from the + detail page at `url` (one item per section card: title, + description, url, tier). + Mirrors layouts/index.json and the dict/slice/append + jsonify style of + layouts/docs/list.llmsitemap.json. All relative URLs are absolutized with + absURL (which leaves already-absolute links like npm/GitHub URLs unchanged). +*/ -}} +{{- $entries := slice -}} +{{- range sort .Params.entries "date" "desc" -}} + {{- if eq .type "updates" -}} + {{- $items := slice -}} + {{- range .items -}} + {{- $item := dict "title" .title "description" .description -}} + {{- with .url -}}{{- $item = merge $item (dict "url" (absURL .)) -}}{{- end -}} + {{- with .tier -}}{{- $item = merge $item (dict "tier" .) -}}{{- end -}} + {{- $items = $items | append $item -}} + {{- end -}} + {{- $entries = $entries | append (dict + "type" "updates" + "label" .label + "date" (time.Format "2006-01-02" .date) + "items" $items) -}} + {{- else if eq .type "release" -}} + {{- with site.GetPage (strings.TrimSuffix "/" .url) -}} + {{- $items := slice -}} + {{- range .Params.sections -}} + {{- range .cards -}} + {{- $item := dict "title" .title "description" (.description | markdownify | plainify | htmlUnescape | chomp | replaceRE "\\s+" " ") -}} + {{- with .link -}}{{- $item = merge $item (dict "url" (absURL .)) -}}{{- end -}} + {{- with .tier -}}{{- $item = merge $item (dict "tier" .) -}}{{- end -}} + {{- $items = $items | append $item -}} + {{- end -}} + {{- end -}} + {{- $entries = $entries | append (dict + "type" "release" + "title" .Title + "label" .Params.label + "date" (time.Format "2006-01-02" .Params.date) + "url" .Permalink + "items" $items) -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- dict + "title" .Title + "url" (absURL .RelPermalink) + "entries" $entries + | jsonify (dict "indent" " ") -}} diff --git a/layouts/releases/rss.xml b/layouts/releases/rss.xml new file mode 100644 index 000000000000..d9848563fe09 --- /dev/null +++ b/layouts/releases/rss.xml @@ -0,0 +1,45 @@ +{{ printf "" | safeHTML }} +{{- /* + Releases RSS feed -> /releases/rss.xml + + Driven by the `entries` manifest in content/releases/_index.md, emitted + newest-first by date. A `release` entry emits one linking to its + detail page (resolved at `url`). An `updates` entry emits one for + each item that carries a url, using the item's own description. Modeled on + layouts/blog/rss.xml and layouts/events/rss.xml. URLs are absolutized with + absURL. +*/ -}} + + + Pulumi Releases + {{ .Permalink }} + A running log of major platform updates from the Pulumi team. + {{ .Site.LanguageCode }} + {{ range sort .Params.entries "date" "desc" }} + {{ if eq .type "release" }} + {{ with site.GetPage (strings.TrimSuffix "/" .url) }} + + {{ .Title }} + {{ .Permalink }} + {{ .Permalink }} + {{ time.Format "Mon, 02 Jan 2006 15:04:05 -0700" .Params.date | safeHTML }} + {{ .Params.short_description | default .Params.label }} + + {{ end }} + {{ else if eq .type "updates" }} + {{ $date := .date }} + {{ range $item := .items }} + {{ with $item.url }} + + {{ $item.title }} + {{ absURL . }} + {{ $date | time.Format "2006-01-02" }}-{{ absURL . }} + {{ time.Format "Mon, 02 Jan 2006 15:04:05 -0700" $date | safeHTML }} + {{ $item.description }} + + {{ end }} + {{ end }} + {{ end }} + {{ end }} + + From 89cfba9142110ea040fa63ea6a9a6f2986efb781 Mon Sep 17 00:00:00 2001 From: Jeff Merrick Date: Wed, 24 Jun 2026 13:40:01 -0500 Subject: [PATCH 2/3] releases: emit one RSS item per release card to match the JSON feed A release entry now expands into one per linked section card on its detail page (title, link, card description), mirroring the JSON feed's items instead of a single release-page item. Co-Authored-By: Claude Opus 4.8 --- layouts/releases/rss.xml | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/layouts/releases/rss.xml b/layouts/releases/rss.xml index d9848563fe09..7680db69e841 100644 --- a/layouts/releases/rss.xml +++ b/layouts/releases/rss.xml @@ -3,11 +3,11 @@ Releases RSS feed -> /releases/rss.xml Driven by the `entries` manifest in content/releases/_index.md, emitted - newest-first by date. A `release` entry emits one linking to its - detail page (resolved at `url`). An `updates` entry emits one for - each item that carries a url, using the item's own description. Modeled on - layouts/blog/rss.xml and layouts/events/rss.xml. URLs are absolutized with - absURL. + newest-first by date, and mirroring the JSON feed's items. An `updates` + entry emits one per item that carries a url. A `release` entry emits + one per linked section card on its detail page (resolved at `url`), + using the card's title, link, and description. Modeled on layouts/blog/rss.xml + and layouts/events/rss.xml. URLs are absolutized with absURL. */ -}} @@ -18,13 +18,20 @@ {{ range sort .Params.entries "date" "desc" }} {{ if eq .type "release" }} {{ with site.GetPage (strings.TrimSuffix "/" .url) }} + {{ $date := .Params.date }} + {{ range .Params.sections }} + {{ range $card := .cards }} + {{ with $card.link }} - {{ .Title }} - {{ .Permalink }} - {{ .Permalink }} - {{ time.Format "Mon, 02 Jan 2006 15:04:05 -0700" .Params.date | safeHTML }} - {{ .Params.short_description | default .Params.label }} + {{ $card.title }} + {{ absURL . }} + {{ $date | time.Format "2006-01-02" }}-{{ absURL . }} + {{ time.Format "Mon, 02 Jan 2006 15:04:05 -0700" $date | safeHTML }} + {{ $card.description | markdownify | plainify | htmlUnescape | chomp | replaceRE "\\s+" " " }} + {{ end }} + {{ end }} + {{ end }} {{ end }} {{ else if eq .type "updates" }} {{ $date := .date }} From d6a2835707734809f8c652755cd0d745c06d464a Mon Sep 17 00:00:00 2001 From: Jeff Merrick Date: Wed, 24 Jun 2026 14:51:45 -0500 Subject: [PATCH 3/3] releases: align GHES wording with docs and drop Neo tier badges Fix the GitHub Enterprise Server update blurb to say "comments" rather than "Neo's writes", matching the linked GitHub App docs. Remove the per-tier badges from Neo items (code reviews, GitHub/Slack apps, scheduled tasks). The upcoming pricing update (cnunciato/pricing-page-content-update) de-gates Neo by tier in favor of token-based metering, so advertising Team/Enterprise gates here would go stale. Genuinely tier-gated badges (private Terraform modules -> Enterprise, GHES auth -> Business Critical) are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- content/releases/_index.md | 3 +-- content/releases/agentic-infrastructure-era.md | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/content/releases/_index.md b/content/releases/_index.md index ffc41432ce5b..b5a22d4c357e 100644 --- a/content/releases/_index.md +++ b/content/releases/_index.md @@ -29,7 +29,6 @@ entries: - title: "Neo code reviews: AI code review built for infrastructure" description: "Neo reviews each pull request as an agent — reading the code, the preview plan, and the resulting infrastructure diff together — and posts inline findings on the affected lines." url: /blog/neo-code-reviews/ - tier: Team - title: "Browse and publish private Terraform modules in the Pulumi Cloud registry" description: "Publish your existing Terraform and OpenTofu modules to the Pulumi Cloud private registry with the tooling you already use, and browse them in the console — a drop-in migration path from HCP Terraform." tier: Enterprise @@ -37,7 +36,7 @@ entries: description: "😎 The Pulumi docs now offer a light, dark, and system theme toggle." url: /docs/ - title: "Individual user authentication for GitHub Enterprise Server" - description: "Self-hosted GitHub Enterprise operations now run as the individual user who triggered them, so pull requests, commits, and Neo's writes are attributed to that person and respect their permissions." + description: "Self-hosted GitHub Enterprise operations now run as the individual user who triggered them, so pull requests, commits, and comments are attributed to that person and respect their permissions." url: /docs/integrations/version-control/github-app/#individual-user-authentication-for-github-enterprise-server tier: Business Critical - title: "One CLI for Pulumi ESC with pulumi env" diff --git a/content/releases/agentic-infrastructure-era.md b/content/releases/agentic-infrastructure-era.md index aa90f98cd352..8081dabd5865 100644 --- a/content/releases/agentic-infrastructure-era.md +++ b/content/releases/agentic-infrastructure-era.md @@ -100,7 +100,6 @@ sections: Now you can @-mention Neo in GitHub issues and pull requests, and in your team's Slack workspace, to kick off full-context infra tasks wherever you are. link: /blog/neo-github-slack/ - tier: Team - variant: text icon: plugs @@ -117,7 +116,6 @@ sections: Use Neo to automate recurring infrastructure tasks like keeping providers updated, identifying non-compliant resources, and summarizing infrastructure changes from week to week. link: /blog/neo-automations/ - tier: Enterprise - anchor: ai-frontier label: Partnering with the frontier of AI infrastructure