Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
142ce79
chore(ci): remove dangling typescript-express-seed-update reference f…
davidkonigsberg Apr 7, 2026
4e918ba
chore(php): update php-sdk seed (#14695)
fern-support Apr 7, 2026
d415016
chore(typescript): update ts-sdk seed (#14705)
fern-support Apr 7, 2026
1a7d3dd
chore(java): update java-model seed (#14706)
fern-support Apr 7, 2026
80db72c
chore(openapi): update openapi seed (#14698)
fern-support Apr 7, 2026
4b43730
chore(php): update php-model seed (#14700)
fern-support Apr 7, 2026
50880c9
fix(deps): update vite to 7.3.2 to address path traversal vulnerabili…
github-actions[bot] Apr 7, 2026
95fdcc0
chore(csharp): update csharp-sdk seed (#14696)
fern-support Apr 7, 2026
39d8b87
chore(rust): update rust-sdk seed (#14697)
fern-support Apr 7, 2026
4a2d2de
chore(swift): update swift-sdk seed (#14699)
fern-support Apr 7, 2026
ecd4a89
chore(ruby): update ruby-sdk-v2 seed (#14703)
fern-support Apr 7, 2026
d7a5502
chore(go): update go-model seed (#14701)
fern-support Apr 7, 2026
fda0d9c
chore(python): update python-sdk seed (#14702)
fern-support Apr 7, 2026
3a4f756
chore(java): update java-sdk seed (#14704)
fern-support Apr 7, 2026
bcdfd4a
chore(csharp): update csharp-model seed (#14707)
fern-support Apr 7, 2026
c243f09
chore(deps): fix defu prototype pollution vulnerability (CVE-2026-352…
github-actions[bot] Apr 7, 2026
7ecb9ee
chore(ci): add Docker Hub auth and layer caching (#14676)
Swimburger Apr 7, 2026
c32e032
fix(ci): use env vars instead of secrets in step if conditions (#14711)
davidkonigsberg Apr 7, 2026
e14a749
fix(python): fix snippet regressions in reference.md and docstrings (…
aditya-arolkar-swe Apr 7, 2026
a3924e3
chore(ci): switch benchmark baseline from actions/cache to artifacts …
devin-ai-integration[bot] Apr 7, 2026
ef58cdc
chore(python): update python-sdk seed (#14714)
fern-support Apr 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 10 additions & 0 deletions .github/actions/auto-update-seed/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ inputs:
description: The matrix strategy index of the job (to differentiate when parallelizing jobs)
required: false
default: "0"
dockerhub-username:
description: Docker Hub username for authenticated pulls (avoids rate limits)
required: false
default: ""
dockerhub-token:
description: Docker Hub token for authenticated pulls (avoids rate limits)
required: false
default: ""

runs:
using: "composite"
Expand All @@ -40,6 +48,8 @@ runs:
allow-unexpected-failures: ${{ inputs.allow-unexpected-failures }}
skip-scripts: ${{ inputs.skip-scripts }}
skip-git-diff-check: true
dockerhub-username: ${{ inputs.dockerhub-username }}
dockerhub-token: ${{ inputs.dockerhub-token }}

# Add changes first to simplify git diff checks, no changes will NOT error here
- name: Check for changes
Expand Down
55 changes: 55 additions & 0 deletions .github/actions/cached-seed/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,52 @@ inputs:
description: Whether to allow unexpected failures during seed generation
required: false
default: "false"
dockerhub-username:
description: Docker Hub username for authenticated pulls (avoids rate limits)
required: false
default: ""
dockerhub-token:
description: Docker Hub token for authenticated pulls (avoids rate limits)
required: false
default: ""

runs:
using: "composite"
steps:
- name: Install
uses: ./.github/actions/install

- name: Log in to Docker Hub
if: ${{ inputs.dockerhub-username != '' }}
uses: docker/login-action@v3
with:
username: ${{ inputs.dockerhub-username }}
password: ${{ inputs.dockerhub-token }}

- name: Clear stale Docker image cache
shell: bash
run: rm -rf /tmp/docker-cache

- name: Cache Docker images
id: docker-cache
uses: actions/cache@v4
with:
path: /tmp/docker-cache
key: ${{ runner.os }}-docker-${{ inputs.generator-name }}-${{ hashFiles('**/Dockerfile*') }}
restore-keys: |
${{ runner.os }}-docker-${{ inputs.generator-name }}-

- name: Load cached Docker images
shell: bash
run: |
for f in /tmp/docker-cache/*.tar; do
[ -f "$f" ] && docker load -i "$f" || true
done

- name: Record pre-existing Docker images
shell: bash
run: docker images -q | sort -u > /tmp/docker-images-before.txt

- name: Seed Test
shell: bash
env:
Expand Down Expand Up @@ -55,3 +94,19 @@ runs:
echo "Seed test failed (attempt $attempt/3), retrying in 10s..."
sleep 10
done

- name: Save Docker images for cache
if: always()
shell: bash
run: |
if [ ! -f /tmp/docker-images-before.txt ]; then
echo "Skipping cache save: docker-images-before.txt not found"
exit 0
fi
mkdir -p /tmp/docker-cache
comm -13 /tmp/docker-images-before.txt <(docker images -q | sort -u) | while read -r id; do
img=$(docker inspect --format '{{index .RepoTags 0}}' "$id" 2>/dev/null || true)
[ -z "$img" ] && continue
fname=$(echo "$img" | tr '/:' '_')
docker save "$img" -o "/tmp/docker-cache/${fname}.tar" 2>/dev/null || true
done
10 changes: 10 additions & 0 deletions .github/actions/run-seed-process/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ inputs:
description: "Whether to skip the git diff check (used by auto-update-seed where changes are expected)"
required: false
default: "false"
dockerhub-username:
description: Docker Hub username for authenticated pulls (avoids rate limits)
required: false
default: ""
dockerhub-token:
description: Docker Hub token for authenticated pulls (avoids rate limits)
required: false
default: ""

outputs:
start_time:
Expand Down Expand Up @@ -78,6 +86,8 @@ runs:
fixtures-to-run: ${{ steps.json-to-string.outputs.as-string }}
skip-scripts: ${{ inputs.skip-scripts }}
allow-unexpected-failures: ${{ inputs.allow-unexpected-failures }}
dockerhub-username: ${{ inputs.dockerhub-username }}
dockerhub-token: ${{ inputs.dockerhub-token }}

- name: Summarize Test Results
if: always() && !cancelled()
Expand Down
19 changes: 16 additions & 3 deletions .github/scripts/format-benchmark-report.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
# {"generator":"ts-sdk","spec":"square","duration_seconds":45}
#
# The main-results-dir may contain either:
# - Flat .jsonl files (single baseline, legacy format)
# - Flat .jsonl files (single baseline) with optional e2e/ subdirectory
# for E2E results (artifacts API format)
# - A history/ subdirectory with dated run folders, each containing .jsonl files.
# When history exists, the median of all historical runs is used as the baseline.
# E2E results are stored in history/<run>/e2e/ subdirectories.
Expand All @@ -26,7 +27,8 @@ compute_median() {
}

# Look up E2E baseline duration for a given generator+spec.
# Reads from history/<run>/e2e/ subdirectories.
# Checks flat e2e/ subdirectory first (artifacts API format),
# then falls back to history/<run>/e2e/ subdirectories.
# Sets: E2E_BASELINE_VAL, E2E_BASELINE_RUNS
lookup_e2e_baseline() {
local generator="$1"
Expand All @@ -51,6 +53,17 @@ lookup_e2e_baseline() {
if [ "$E2E_BASELINE_RUNS" -gt 0 ]; then
E2E_BASELINE_VAL=$(printf '%s\n' "${durations[@]}" | compute_median)
fi
else
# Flat E2E layout: MAIN_DIR/e2e/{generator}.jsonl (artifacts API format)
local e2e_file="${MAIN_DIR}/e2e/${generator}.jsonl"
if [ -f "$e2e_file" ]; then
local dur
dur=$(jq -r --arg spec "$spec" 'select(.spec == $spec) | .duration_seconds' "$e2e_file" 2>/dev/null || true)
if [ -n "$dur" ] && [ "$dur" != "null" ] && [ "$dur" != "0" ]; then
E2E_BASELINE_VAL="$dur"
E2E_BASELINE_RUNS=1
fi
fi
fi
}

Expand Down Expand Up @@ -105,7 +118,7 @@ if [ -n "${BASELINE_TIMESTAMP:-}" ]; then
HISTORY_COUNT=$(ls -d "${MAIN_DIR}/history"/*/ 2>/dev/null | wc -l)
echo "Comparing PR branch against **median of ${HISTORY_COUNT} nightly run(s)** on \`main\` (latest: ${BASELINE_TIMESTAMP})."
else
echo "Comparing PR branch against cached \`main\` baseline (generated ${BASELINE_TIMESTAMP})."
echo "Comparing PR branch against latest nightly baseline on \`main\` (${BASELINE_TIMESTAMP})."
fi
else
echo "Comparing PR branch against \`main\` baseline."
Expand Down
21 changes: 20 additions & 1 deletion .github/scripts/test-format-benchmark-report.sh
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ test_baseline_timestamp() {

OUTPUT=$(BASELINE_TIMESTAMP="2026-03-30T04:00:00Z" bash "$REPORT_SCRIPT" "$PR_DIR" "$MAIN_DIR")

assert_contains "$OUTPUT" "cached" "Shows cached baseline label"
assert_contains "$OUTPUT" "latest nightly baseline" "Shows nightly baseline label"
assert_contains "$OUTPUT" "2026-03-30T04:00:00Z" "Shows baseline timestamp"
assert_contains "$OUTPUT" "benchmark-baseline" "Shows link to refresh workflow"
}
Expand Down Expand Up @@ -522,6 +522,25 @@ test_e2e_column_with_history
test_e2e_column_missing
test_e2e_partial_coverage
test_e2e_median_odd

# Test 22: E2E column from flat e2e/ directory (artifacts API layout)
test_e2e_flat_layout() {
echo "Test: E2E column populated from flat e2e/ directory (artifacts API)"
setup_dirs
echo '{"generator":"ts-sdk","spec":"square","duration_seconds":50,"exit_code":0}' > "$PR_DIR/ts-sdk.jsonl"

# Flat layout: generator-only at root, E2E in e2e/ subdir
echo '{"generator":"ts-sdk","spec":"square","duration_seconds":45,"exit_code":0}' > "$MAIN_DIR/ts-sdk.jsonl"
mkdir -p "$MAIN_DIR/e2e"
echo '{"generator":"ts-sdk","spec":"square","duration_seconds":200,"exit_code":0}' > "$MAIN_DIR/e2e/ts-sdk.jsonl"

OUTPUT=$(BASELINE_TIMESTAMP="2026-04-06T04:00:00Z" bash "$REPORT_SCRIPT" "$PR_DIR" "$MAIN_DIR")

assert_contains "$OUTPUT" "| ts-sdk | square | 45s | 200s | 50s |" "Flat layout: both gen and E2E shown"
assert_contains "$OUTPUT" "latest nightly baseline" "Flat layout: shows nightly baseline label"
}

test_e2e_flat_layout
echo ""
echo "=== PostHog JSON validation tests ==="
echo ""
Expand Down
97 changes: 2 additions & 95 deletions .github/workflows/benchmark-baseline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ jobs:
with:
name: benchmark-baseline-${{ matrix.generator }}
path: benchmark-results/
retention-days: 7
retention-days: 30

# Run full E2E benchmarks (with build/test scripts) for informational purposes.
# These capture the complete customer-observable generation time including
Expand Down Expand Up @@ -116,100 +116,7 @@ jobs:
with:
name: benchmark-e2e-${{ matrix.generator }}
path: benchmark-results/
retention-days: 7

# Aggregate results into rolling 7-day history cache
save-baseline-cache:
runs-on: ubuntu-latest
timeout-minutes: 10
needs: [baseline, baseline-e2e]
if: ${{ always() && !cancelled() && needs.baseline.result != 'skipped' }}
steps:
- name: Download generator-only baseline artifacts
uses: actions/download-artifact@v4
with:
pattern: benchmark-baseline-*
path: todays-results
merge-multiple: true

- name: Download E2E baseline artifacts
uses: actions/download-artifact@v4
with:
pattern: benchmark-e2e-*
path: todays-e2e-results
merge-multiple: true

- name: Restore existing history cache
id: history-cache
uses: actions/cache/restore@v4
with:
path: benchmark-baseline-results
# Prefix fallback finds the most recent history cache entry
key: benchmark-baseline-never-matches
restore-keys: benchmark-baseline-

- name: Append today's results and prune old entries
shell: bash
run: |
mkdir -p benchmark-baseline-results/history
TODAY=$(date -u +%Y-%m-%d)
RUN_DIR="benchmark-baseline-results/history/${TODAY}_${{ github.run_id }}"
mkdir -p "$RUN_DIR"

# Copy generator-only results (used for PR comparisons)
if [ -d todays-results ] && ls todays-results/*.jsonl 1>/dev/null 2>&1; then
for f in todays-results/*.jsonl; do
[ -f "$f" ] && cp "$f" "$RUN_DIR/"
done
else
echo "::warning::No generator-only baseline results found. Removing empty history entry."
rm -rf "$RUN_DIR"
echo "SKIP_CACHE_SAVE=true" >> "$GITHUB_ENV"
fi

# Only proceed with E2E + metadata if generator-only results were found
if [ -d "$RUN_DIR" ]; then
# Copy full E2E results into a separate subdirectory (informational only)
if [ -d todays-e2e-results ] && ls todays-e2e-results/*.jsonl 1>/dev/null 2>&1; then
mkdir -p "$RUN_DIR/e2e"
for f in todays-e2e-results/*.jsonl; do
[ -f "$f" ] && cp "$f" "$RUN_DIR/e2e/"
done
fi

# Store metadata for this run
echo "{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"sha\":\"${{ github.sha }}\"}" \
> "$RUN_DIR/_metadata.json"
fi

# Prune history older than 7 days
# Note: 'date -d' is GNU coreutils syntax (CI runs on Ubuntu; not portable to macOS)
CUTOFF=$(date -u -d '7 days ago' +%Y-%m-%d)
for dir in benchmark-baseline-results/history/*/; do
[ -d "$dir" ] || continue
DIR_DATE=$(basename "$dir" | cut -d_ -f1)
if [[ "$DIR_DATE" < "$CUTOFF" ]]; then
echo "Pruning old baseline: $dir (date: $DIR_DATE, cutoff: $CUTOFF)"
rm -rf "$dir"
fi
done

# Write top-level metadata summarizing the history
RUNS=$(ls -d benchmark-baseline-results/history/*/ 2>/dev/null | wc -l)
echo "{\"latest_timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"sha\":\"${{ github.sha }}\",\"total_runs\":${RUNS}}" \
> benchmark-baseline-results/_metadata.json

echo "Baseline history summary:"
cat benchmark-baseline-results/_metadata.json
echo "History entries:"
ls -la benchmark-baseline-results/history/

- name: Save baseline to cache
if: ${{ env.SKIP_CACHE_SAVE != 'true' }}
uses: actions/cache/save@v4
with:
path: benchmark-baseline-results
key: benchmark-baseline-${{ github.run_id }}
retention-days: 30

# Send benchmark results to PostHog for long-term trend analysis and dashboards.
# Uses the same PostHog project + API key as seed test metrics (seed.yml).
Expand Down
45 changes: 45 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,36 @@ jobs:
- name: Install
uses: ./.github/actions/install

- name: Log in to Docker Hub
if: ${{ env.DOCKERHUB_USERNAME != '' }}
uses: docker/login-action@v3
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }}
with:
username: ${{ secrets.DOCKER_USERNAME_PUBLIC_READONLY }}
password: ${{ secrets.DOCKER_PASSWORD_PUBLIC_READONLY }}

- name: Clear stale Docker image cache
run: rm -rf /tmp/docker-cache

- name: Cache Docker images
id: docker-cache
uses: actions/cache@v4
with:
path: /tmp/docker-cache
key: ${{ runner.os }}-docker-ete-${{ hashFiles('**/Dockerfile*') }}
restore-keys: |
${{ runner.os }}-docker-ete-

- name: Load cached Docker images
run: |
for f in /tmp/docker-cache/*.tar; do
[ -f "$f" ] && docker load -i "$f" || true
done

- name: Record pre-existing Docker images
run: docker images -q | sort -u > /tmp/docker-images-before.txt

- name: Cache Fern bin dependencies
uses: actions/cache@v4
with:
Expand Down Expand Up @@ -232,6 +262,21 @@ jobs:
echo "No snapshot changes to commit"
fi

- name: Save Docker images for cache
if: always()
run: |
if [ ! -f /tmp/docker-images-before.txt ]; then
echo "Skipping cache save: docker-images-before.txt not found"
exit 0
fi
mkdir -p /tmp/docker-cache
comm -13 /tmp/docker-images-before.txt <(docker images -q | sort -u) | while read -r id; do
img=$(docker inspect --format '{{index .RepoTags 0}}' "$id" 2>/dev/null || true)
[ -z "$img" ] && continue
fname=$(echo "$img" | tr '/:' '_')
docker save "$img" -o "/tmp/docker-cache/${fname}.tar" 2>/dev/null || true
done

- name: Ensure no changes to git-tracked files
if: ${{ !github.event.inputs.update_snapshots }}
run: git --no-pager diff --exit-code
Expand Down
Loading
Loading