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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
125 changes: 125 additions & 0 deletions .github/scripts/api-surface-diff.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/env bash
# Summarise how each SDK's public API surface changed relative to the PR's base
# branch, as Markdown for a PR comment and the job summary.
#
# For each published package it diffs the base branch's committed api-report
# against the surface regenerated from this PR's code (temp/, written by
# `api-extractor run`). Using the regenerated file means the comment is right
# even when the PR forgot to re-baseline the committed snapshot; that case is
# called out separately.
#
# Usage: api-surface-diff.sh <base-ref> <output.md>
# base-ref e.g. origin/main (must already be fetched)
# output.md where to write the Markdown
set -uo pipefail

BASE_REF="${1:?usage: api-surface-diff.sh <base-ref> <output.md>}"
OUT="${2:?usage: api-surface-diff.sh <base-ref> <output.md>}"

# Keep the comment under GitHub's 65,536-character limit.
MAX_DIFF_LINES_PER_PACKAGE=350

# name | committed report | regenerated report | legacy path of the report on
# pre-split base branches (main still has the skyvault report at the repo root)
PACKAGES=(
"skyflow-node|skyvault/api-report/skyflow-node.api.md|skyvault/temp/skyflow-node.api.md|api-report/skyflow-node.api.md"
"skyflow-flowvault-node|flowvault/api-report/skyflow-flowvault-node.api.md|flowvault/temp/skyflow-flowvault-node.api.md|"
)

{
echo "<!-- api-surface-diff -->"
echo "## Public API surface changes"
echo ""
echo "Compared with \`${BASE_REF#origin/}\`. In the diffs, \`-\` lines were removed"
echo "from the public API and \`+\` lines were added. Removals and signature"
echo "changes are breaking for consumers."
echo ""
} > "$OUT"

any_changes=0
stale_snapshot=0

for entry in "${PACKAGES[@]}"; do
IFS='|' read -r name report temp legacy <<< "$entry"

# Prefer the regenerated surface; fall back to the committed snapshot if the
# build never got that far.
current="$temp"
if [ ! -f "$current" ]; then
current="$report"
fi
if [ ! -f "$current" ]; then
{
echo "### $name"
echo ""
echo "No API report was produced for this package (the build failed before api-extractor ran)."
echo ""
} >> "$OUT"
continue
fi

if [ -f "$temp" ] && [ -f "$report" ] && ! cmp -s "$temp" "$report"; then
stale_snapshot=1
fi

base_file="$(mktemp)"
base_note=""
if git cat-file -e "$BASE_REF:$report" 2>/dev/null; then
git show "$BASE_REF:$report" > "$base_file"
elif [ -n "$legacy" ] && git cat-file -e "$BASE_REF:$legacy" 2>/dev/null; then
git show "$BASE_REF:$legacy" > "$base_file"
base_note=" _(base report read from its old location \`$legacy\`)_"
else
: > "$base_file"
base_note=" _(no report on the base branch, so every line is new)_"
fi

diff_out="$(diff -u --label "base/$name.api.md" --label "pr/$name.api.md" "$base_file" "$current" || true)"
rm -f "$base_file"

if [ -z "$diff_out" ]; then
{
echo "### $name: no changes"
echo ""
} >> "$OUT"
continue
fi

any_changes=1
added="$(printf '%s\n' "$diff_out" | grep -cE '^\+[^+]' || true)"
removed="$(printf '%s\n' "$diff_out" | grep -cE '^-[^-]' || true)"
total_lines="$(printf '%s\n' "$diff_out" | wc -l | tr -d ' ')"

{
echo "### $name: ${added} added, ${removed} removed${base_note}"
echo ""
echo "<details><summary>Show diff</summary>"
echo ""
echo '```diff'
printf '%s\n' "$diff_out" | head -n "$MAX_DIFF_LINES_PER_PACKAGE"
if [ "$total_lines" -gt "$MAX_DIFF_LINES_PER_PACKAGE" ]; then
echo ""
echo "... $((total_lines - MAX_DIFF_LINES_PER_PACKAGE)) more lines. The full reports are in the \`api-surface-reports\` workflow artifact."
fi
echo '```'
echo ""
echo "</details>"
echo ""
} >> "$OUT"
done

if [ "$stale_snapshot" -eq 1 ]; then
{
echo "> **Snapshot out of date.** The committed \`api-report\` does not match the"
echo "> API this PR builds, so the Contract Tests check fails. If the change is"
echo "> intended, run \`npm run contract-snapshot\` and commit the updated reports."
echo ""
} >> "$OUT"
fi

if [ "$any_changes" -eq 0 ]; then
{
echo "No public API changes in this PR."
echo ""
} >> "$OUT"
fi
4 changes: 3 additions & 1 deletion .github/workflows/beta-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ jobs:
build-and-deploy:
uses: ./.github/workflows/common-release.yml
with:
tag: beta
product: skyflow-node
package-dir: skyvault
release-type: beta
secrets: inherit
7 changes: 6 additions & 1 deletion .github/workflows/common-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ jobs:
- name: Run ESLint
run: npm run eslint

# One command builds BOTH SDKs (common -> skyvault/flowvault via project references).
- name: Build all packages
run: npm run build

# Fans out `npm test` across every workspace (common, skyvault, flowvault).
- name: Run Tests
env:
SA_WITHOUT_CONTEXT: ${{ secrets.SA_WITHOUT_CONTEXT }}
Expand All @@ -33,6 +38,6 @@ jobs:
uses: codecov/codecov-action@v2.1.0
with:
token: ${{ secrets.CODECOV_REPO_UPLOAD_TOKEN }}
files: coverage/lcov.info
files: skyvault/coverage/lcov.info,flowvault/coverage/lcov.info
name: codecov-skyflow-node
verbose: true
108 changes: 62 additions & 46 deletions .github/workflows/common-release.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
name: Common Release Workflow

# Reusable release pipeline, parameterized per product.
# product npm package name (skyflow-node | skyflow-flowvault-node)
# package-dir workspace dir (skyvault | flowvault)
# release-type public | beta | internal
# version semver to release (caller strips any tag prefix, e.g. flowvault/1.0.0 -> 1.0.0)

on:
workflow_call:
inputs:
tag:
product:
required: true
type: string
package-dir:
required: true
type: string
release-type:
required: true
type: string
version:
required: false
type: string

jobs:
build-sdk:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
Expand All @@ -24,72 +38,74 @@ jobs:
- name: Install Packages
run: npm install --ignore-scripts

- name: Build
run: npm run build
- name: Resolve Version
id: version
run: |
if [ -n "${{ inputs.version }}" ]; then
echo "value=${{ inputs.version }}" >> $GITHUB_OUTPUT
else
# Fallback: strip an optional "<dir>/" prefix from the pushed tag.
echo "value=${GITHUB_REF_NAME##*/}" >> $GITHUB_OUTPUT
fi

- name: Get Previous Tag
id: previoustag
uses: WyriHaximus/github-action-get-previous-tag@v1
with:
fallback: 1.0.0
- name: Bump Version (${{ inputs.product }})
run: |
chmod +x ./scripts/bump_version.sh
if [[ "${{ inputs.release-type }}" == "internal" ]]; then
./scripts/bump_version.sh "${{ inputs.product }}" "${{ steps.version.outputs.value }}" "$(git rev-parse --short "$GITHUB_SHA")"
else
./scripts/bump_version.sh "${{ inputs.product }}" "${{ steps.version.outputs.value }}"
fi

# Build everything (common -> layers) then bundle the published package
# (inlines skyflow-common into a self-contained dist/).
- name: Build
run: |
npm run build
npm run build:dist --workspace ${{ inputs.package-dir }}

- name: Resolve Branch Name
id: resolve-branch
if: ${{ inputs.tag == 'beta' || inputs.tag == 'public' }}
if: ${{ inputs.release-type == 'beta' || inputs.release-type == 'public' }}
run: |
TAG_COMMIT=$(git rev-list -n 1 ${{ github.ref_name }})

BRANCH_NAME=$(git branch -r --contains $TAG_COMMIT | grep -o 'origin/.*' | sed 's|origin/||' | head -n 1)

if [ -z "$BRANCH_NAME" ]; then
echo "Error: Could not resolve branch for the tag."
exit 1
fi

echo "Resolved Branch Name: $BRANCH_NAME"
echo "branch_name=$BRANCH_NAME" >> $GITHUB_ENV

- name: Bump Version
run: |
chmod +x ./scripts/bump_version.sh
if ${{ inputs.tag == 'internal' }}; then
./scripts/bump_version.sh "${{ steps.previoustag.outputs.tag }}" "$(git rev-parse --short "$GITHUB_SHA")"
else
./scripts/bump_version.sh "${{ steps.previoustag.outputs.tag }}"
fi

# Only public/beta record the released version back on the source branch.
# Internal (JFrog) builds publish the bumped working tree directly and do
# NOT push back: the branch needs no mutation, pushing would need write
# access to the release branch, and src/version.ts is not path-ignored so
# a push-back would re-trigger this workflow in a loop.
- name: Commit Changes
if: ${{ inputs.release-type == 'beta' || inputs.release-type == 'public' }}
run: |
git config user.name ${{ github.actor }}
git config user.email ${{ github.actor }}@users.noreply.github.com
if [[ "${{ inputs.tag }}" == "beta" || "${{ inputs.tag }}" == "public" ]]; then
git checkout ${{ env.branch_name }}
fi
git add package.json
if [[ "${{ inputs.tag }}" == "beta" || "${{ inputs.tag }}" == "public" ]]; then
git commit -m "[AUTOMATED] Release - ${{ steps.previoustag.outputs.tag }}"
git push origin ${{ env.branch_name }}
elif [[ "${{ inputs.tag }}" == "internal" ]]; then
git commit -m "[AUTOMATED] Private Release ${{ steps.previoustag.outputs.tag }}-dev.$(git rev-parse --short $GITHUB_SHA)"
git push origin ${{ github.ref_name }} -f
fi
git checkout ${{ env.branch_name }}
git add ${{ inputs.package-dir }}/package.json ${{ inputs.package-dir }}/src/version.ts
git commit -m "[AUTOMATED] Release ${{ inputs.product }} - ${{ steps.version.outputs.value }}" || echo "nothing to commit"
git push origin ${{ env.branch_name }}

- name: Publish
working-directory: ${{ inputs.package-dir }}
run: |
if [[ "${{ inputs.tag }}" == "beta" ]]; then
if [[ "${{ inputs.release-type }}" == "beta" ]]; then
npm publish --tag beta
elif [[ "${{ inputs.tag }}" == "public" ]]; then
MAJOR_VERSION=$(node -p "require('./package.json').version.split('.')[0]")
if [[ "$MAJOR_VERSION" == "1" ]]; then
npm publish --tag v1
else
npm publish
fi
elif [[ "${{ inputs.tag }}" == "internal" ]]; then
JFROG_AUTH=$(echo -n "$JFROG_USERNAME:$JFROG_PASSWORD" | base64 -w 0)
npm config set registry https://prekarilabs.jfrog.io/prekarilabs/api/npm/npm/
npm config set //prekarilabs.jfrog.io/prekarilabs/api/npm/npm/:_auth "$JFROG_AUTH"
elif [[ "${{ inputs.release-type }}" == "public" ]]; then
npm publish
elif [[ "${{ inputs.release-type }}" == "internal" ]]; then
# Publish to the internal JFrog registry. npm ignores a workspace
# member's own .npmrc and `npm config set` can't run inside a
# workspace dir (ENOWORKSPACES), so pass the registry on the command
# line and add its auth to the workspace-root .npmrc that npm reads.
JFROG_AUTH=$(echo -n "$JFROG_USERNAME:$JFROG_PASSWORD" | base64 -w 0)
echo "//prekarilabs.jfrog.io/prekarilabs/api/npm/npm/:_auth=${JFROG_AUTH}" >> "$GITHUB_WORKSPACE/.npmrc"
npm publish --registry https://prekarilabs.jfrog.io/prekarilabs/api/npm/npm/
fi
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Expand Down
Loading
Loading