Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions content/releases/_index.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,57 @@
---
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 <details> 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/
- 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 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"
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/
---
5 changes: 4 additions & 1 deletion layouts/partials/releases/card-text-block.html
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}

<div class="flex flex-col gap-3">
{{ with $icon }}
Expand All @@ -29,7 +32,7 @@
</div>
{{ end }}
{{ with $title }}
<h3 class="heading-2 font-display font-semibold tracking-tighter text-service-black m-0 leading-[1.1]">{{ . }}</h3>
<h3 class="heading-2 font-display font-semibold tracking-tighter text-service-black m-0 leading-[1.1]">{{ . }}{{ with $tier }}<span class="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 transition-colors group-hover/linked:bg-violet-100 group-hover/linked:text-violet-primary">{{ . }}</span>{{ end }}</h3>
{{ end }}
{{ with $description }}
<div class="body-base text-service-black leading-6">
Expand Down
3 changes: 2 additions & 1 deletion layouts/partials/releases/card.html
Original file line number Diff line number Diff line change
Expand Up @@ -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" }}
Expand All @@ -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 }}<a href="{{ $link }}" class="{{ $cardClasses }}">{{ else }}<div class="{{ $cardClasses }}">{{ end }}

Expand Down
44 changes: 44 additions & 0 deletions layouts/partials/releases/updates.html
Original file line number Diff line number Diff line change
@@ -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 <details> disclosure: the <summary> 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 <summary> as
the toggle keeps the link a plain in-body <a>, 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" }}

<section class="p-8 w-full max-w-6xl mx-auto">
{{ with $label }}
<p class="font-mono text-xs tracking-wider uppercase text-service-black m-0 mb-4">{{ . }}</p>
{{ end }}

<ul class="list-none p-0 m-0 flex flex-col">
{{ range $items }}
<li class="m-0 border-b border-gray-200 last:border-b-0">
<details class="group/item">
<summary class="flex items-center gap-2 py-4 cursor-pointer list-none [&::-webkit-details-marker]:hidden heading-4 mb-0 text-service-black hover:text-violet-primary transition-colors">
<span>{{ .title }}</span>
{{ with .tier }}<span class="{{ $badgeClasses }}">{{ . }}</span>{{ end }}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor" aria-hidden="true" class="ml-auto size-5 shrink-0 text-gray-400 transition-transform duration-200 group-open/item:rotate-180 group-hover/item:text-violet-primary"><path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z"/></svg>
</summary>
<div class="pb-4 -mt-1 max-w-3xl">
<p class="m-0 text-sm leading-relaxed">{{ .description }}{{ with .url }} <a href="{{ . }}" class="font-medium text-violet-primary no-underline hover:underline whitespace-nowrap">Read more<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor" aria-hidden="true" class="inline-block align-middle ml-1 size-4"><path d="M221.66,133.66l-72,72a8,8,0,0,1-11.32-11.32L196.69,136H40a8,8,0,0,1,0-16H196.69L138.34,61.66a8,8,0,0,1,11.32-11.32l72,72A8,8,0,0,1,221.66,133.66Z"/></svg></a>{{ end }}</p>
</div>
</details>
</li>
{{ end }}
</ul>
</section>
31 changes: 21 additions & 10 deletions layouts/releases/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,27 @@ <h1 class="heading-xl font-display font-semibold m-0 md:my-4">{{ .Title }}</h1>
</header>

<div class="flex flex-col gap-8">
{{ $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 }}
</div>

Expand Down
56 changes: 56 additions & 0 deletions layouts/releases/list.json
Original file line number Diff line number Diff line change
@@ -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" " ") -}}
52 changes: 52 additions & 0 deletions layouts/releases/rss.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{{ printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>" | safeHTML }}
{{- /*
Releases RSS feed -> /releases/rss.xml

Driven by the `entries` manifest in content/releases/_index.md, emitted
newest-first by date, and mirroring the JSON feed's items. An `updates`
entry emits one <item> per item that carries a url. A `release` entry emits
one <item> 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.
*/ -}}
<rss version="2.0">
<channel>
<title>Pulumi Releases</title>
<link>{{ .Permalink }}</link>
<description>A running log of major platform updates from the Pulumi team.</description>
<language>{{ .Site.LanguageCode }}</language>
{{ 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 }}
<item>
<title>{{ $card.title }}</title>
<link>{{ absURL . }}</link>
<guid isPermaLink="false">{{ $date | time.Format "2006-01-02" }}-{{ absURL . }}</guid>
<pubDate>{{ time.Format "Mon, 02 Jan 2006 15:04:05 -0700" $date | safeHTML }}</pubDate>
<description>{{ $card.description | markdownify | plainify | htmlUnescape | chomp | replaceRE "\\s+" " " }}</description>
</item>
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ else if eq .type "updates" }}
{{ $date := .date }}
{{ range $item := .items }}
{{ with $item.url }}
<item>
<title>{{ $item.title }}</title>
<link>{{ absURL . }}</link>
<guid isPermaLink="false">{{ $date | time.Format "2006-01-02" }}-{{ absURL . }}</guid>
<pubDate>{{ time.Format "Mon, 02 Jan 2006 15:04:05 -0700" $date | safeHTML }}</pubDate>
<description>{{ $item.description }}</description>
</item>
{{ end }}
{{ end }}
{{ end }}
{{ end }}
</channel>
</rss>
Loading