Skip to content
Open
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
172 changes: 172 additions & 0 deletions .github/workflows/publish-v1-specs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
name: Publish v1 Private Preview Specs

# Publishes v1 private-preview OpenAPI specs for a given API version and cuts a
# release, mirroring the `beta` path of pay-server's api-codegen/tools/publish.sh.
# Specs are pulled from the Statics Commander CDN instead of a local build dir.
#
# Triggered by the zoolander LockAndReleasePrivatePreviewSpecs workflow.

on:
workflow_dispatch:
inputs:
version:
description: 'Version name (e.g., 2026-07-08.preview)'
required: true
type: string
publish_release:
description: 'Publish to master and create a tag; otherwise push to dry-run/private-preview'
required: false
type: boolean
default: false

permissions: {}

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: write
env:
CDN_DIR: private_preview
SPEC_FILES: spec3.private_preview.sdk.yaml spec3.private_preview.sdk.json
FIXTURE_FILES: fixtures3.private_preview.yaml fixtures3.private_preview.json
FIXTURE_MSG: Update fixture data for private preview
SPEC_MSG: Update OpenAPI specification for private preview
TAG_MSG: This release only includes changes to the private preview spec.
TEST_BRANCH: dry-run/private-preview
steps:
- name: Fetch app installation token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
id: gh-api-token
with:
app-id: ${{ secrets.GH_APP_STRIPE_OPENAPI_APP_ID }}
private-key: ${{ secrets.GH_APP_STRIPE_OPENAPI_PRIVATE_KEY }}
# The token checks out this repository and pushes the published specs
# back to it. Scope defaults to the current repository.
permission-contents: write

- name: Checkout openapi
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
token: ${{ steps.gh-api-token.outputs.token }}
# Full history + tags are required so `git describe --tags` can find the
# last release tag and `git show <tag>:...` can read the prior spec.
fetch-depth: 0
fetch-tags: true
# The final step uses plain `git push`, so retain the narrowly scoped
# App token. This workflow uploads no artifacts that could contain it.

- name: Checkout sdk-codegen (provides the diff CLI)
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: stripe/sdk-codegen
ref: 1b117c3457c094060636cdfc7936e30a86b2e3ca # v17.0.0
path: sdk-codegen
persist-credentials: false

- name: Setup just
uses: extractions/setup-just@dd310ad5a97d8e7b41793f8ef055398d51ad4de6 # v2

- name: Setup node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '24'

- name: Download specs from CDN
env:
VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
BASE="https://b.stripecdn.com/api-artifacts/assets/openapi/${VERSION}/${CDN_DIR}"

# Required specs — fail the job if absent.
for f in $SPEC_FILES; do
curl -fsSL "$BASE/$f" -o "openapi/$f"
done

# Optional fixtures — skip cleanly if the CDN returns 404.
for f in $FIXTURE_FILES; do
if ! curl -fsSL "$BASE/$f" -o "openapi/$f"; then
echo "Optional file $f absent from CDN; skipping."
rm -f "openapi/$f"
fi
done

- name: Commit specs, generate diffs, and cut release
env:
PUBLISH_RELEASE: ${{ inputs.publish_release }}
run: |
set -euo pipefail

git config user.name "Stripe OpenAPI"
git config user.email "105521251+stripe-openapi[bot]@users.noreply.github.com"

# --- Commit fixtures (if changed) ---
for f in $FIXTURE_FILES; do
if [ -f "openapi/$f" ]; then git add "openapi/$f"; fi
done
if [ -n "$(git diff --name-only --staged)" ]; then
git commit -m "$FIXTURE_MSG"
fi

# --- Commit spec (if changed); only cut a release when the spec moved ---
for f in $SPEC_FILES; do
git add "openapi/$f"
done
if [ -n "$(git diff --name-only --staged)" ]; then
git commit -m "$SPEC_MSG"

LAST_TAG=$(git describe --tags --abbrev=0)
NEW_TAG="v$(( ${LAST_TAG#v} + 1 ))"
echo "Previous tag ${LAST_TAG}; new tag ${NEW_TAG}"

# Diff the GA spec (old tag vs. working tree), exactly as publish.sh does.
SPEC_PATH="openapi/spec3.sdk.yaml"
OLD_SPEC="/tmp/oldspec.sdk.yaml"
git show "${LAST_TAG}:${SPEC_PATH}" > "$OLD_SPEC"

DIFF_DIR="${GITHUB_WORKSPACE}/openapi/upcoming-changes"
NEW_SPEC="${GITHUB_WORKSPACE}/${SPEC_PATH}"
mkdir -p "$DIFF_DIR"

# Build the sdk-codegen CLI once. `diff` delegates entirely to `main`,
# so call `main` without its build dependency for each generated diff.
cd "${GITHUB_WORKSPACE}/sdk-codegen"
just build-tools

# `rest.md` is the language-agnostic diff (no --lang), matching publish.sh.
just --no-deps main diff --old-path "$OLD_SPEC" --new-path "$NEW_SPEC" --output-path "$DIFF_DIR/rest.md"
for lang in go php node ruby java python dotnet; do
just --no-deps main diff --lang "$lang" --old-path "$OLD_SPEC" --new-path "$NEW_SPEC" --output-path "$DIFF_DIR/$lang.md"
done
cd "${GITHUB_WORKSPACE}"

# --- Commit upcoming-changes (if changed) ---
git add ./openapi/upcoming-changes
if [ -n "$(git diff --name-only --staged)" ]; then
git commit -m "Update upcoming changes"
fi

if [ "$PUBLISH_RELEASE" != "true" ]; then
# Never create/push a real tag in a dry run — a stray vN tag would corrupt
# the next real run's `git describe` increment.
echo "Dry run: would create tag ${NEW_TAG} and push master."
else
git tag "$NEW_TAG" -m "$TAG_MSG"
# Push branch and tag atomically so we never leave a dangling tag.
git push --atomic origin master "$NEW_TAG"
fi
else
echo "No private-preview spec changes; skipping release."
fi

if [ "$PUBLISH_RELEASE" != "true" ]; then
# Push all local commits to a disposable scratch branch for inspection
# instead of master (master and tags are left untouched). Force is expected:
# each dry run builds a fresh history off the current master.
echo "Dry run: force-pushing local commits to ${TEST_BRANCH} for inspection."
git push --force origin "HEAD:refs/heads/${TEST_BRANCH}"
else
# Catch-all: pushes a fixtures-only commit; no-op if the atomic push already ran.
git push origin master
fi