Skip to content

Release

Release #31

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
tag:
description: 'Tag to release (must exist, created by bump-version.sh)'
required: true
type: string
permissions:
contents: write
id-token: write
attestations: write
actions: read
jobs:
# Job 1: Validate tag and resolve commit
prepare:
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.version.outputs.tag }}
version: ${{ steps.version.outputs.version }}
commit: ${{ steps.version.outputs.commit }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Validate tag and version
id: version
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="${{ inputs.tag }}"
# Validate tag format
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::Invalid tag format: $TAG. Expected: v1.2.3"
exit 1
fi
VERSION="${TAG#v}"
# Verify tag exists locally (should be created by bump-version.sh)
if ! git rev-parse "$TAG" >/dev/null 2>&1; then
echo "::error::Tag $TAG not found. Run bump-version.sh first and push the tag."
exit 1
fi
COMMIT=$(git rev-list -n 1 "$TAG")
# Verify package.json version matches tag
PKG_VERSION=$(node -p "require('./npm/webcodecs-ffmpeg/package.json').version")
if [[ "$PKG_VERSION" != "$VERSION" ]]; then
echo "::error::package.json version ($PKG_VERSION) doesn't match tag ($VERSION)"
exit 1
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "commit=$COMMIT" >> "$GITHUB_OUTPUT"
echo "Releasing $TAG (version $VERSION) at commit $COMMIT"
- uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
- name: Verify version not published
run: |
VERSION="${{ steps.version.outputs.version }}"
PKG="@pproenca/webcodecs-ffmpeg"
# Check if version already exists on npm
if npm view "${PKG}@${VERSION}" version 2>/dev/null; then
echo "::error::Version $VERSION already published to npm"
exit 1
fi
echo "Version $VERSION not yet published - OK"
# Job 2: Find CI artifacts
check-artifacts:
needs: prepare
runs-on: ubuntu-latest
outputs:
run_id: ${{ steps.search.outputs.run_id }}
steps:
- name: Search for CI artifacts
id: search
env:
GH_TOKEN: ${{ github.token }}
COMMIT: ${{ needs.prepare.outputs.commit }}
run: |
echo "Searching for CI artifacts..."
RUN_ID=$(gh api "repos/${{ github.repository }}/actions/workflows/ci.yml/runs" \
--jq ".workflow_runs[] | select(.head_sha == \"$COMMIT\" and .conclusion == \"success\") | .id" \
2>/dev/null | head -1 || echo "")
if [[ -n "$RUN_ID" ]]; then
echo "Found CI run for commit $COMMIT: $RUN_ID"
else
echo "No CI run for commit $COMMIT, searching for most recent..."
RUN_ID=$(gh api "repos/${{ github.repository }}/actions/workflows/ci.yml/runs?branch=master&status=success&per_page=1" \
--jq ".workflow_runs[0].id" 2>/dev/null || echo "")
if [[ -z "$RUN_ID" ]]; then
echo "::error::No successful CI run found. Run CI workflow first."
exit 1
fi
RUN_COMMIT=$(gh api "repos/${{ github.repository }}/actions/runs/$RUN_ID" --jq ".head_sha")
echo "Using CI run $RUN_ID from commit $RUN_COMMIT"
fi
# Verify artifacts exist
ARTIFACT_COUNT=$(gh api "repos/${{ github.repository }}/actions/runs/$RUN_ID/artifacts" \
--jq '[.artifacts[] | select(.name | startswith("ffmpeg-"))] | length')
if [[ "$ARTIFACT_COUNT" -lt 8 ]]; then
echo "::error::Expected 8 artifacts, found $ARTIFACT_COUNT. CI artifacts may have expired."
exit 1
fi
echo "Found $ARTIFACT_COUNT artifacts in CI run $RUN_ID"
echo "run_id=$RUN_ID" >> "$GITHUB_OUTPUT"
# Job 3: Create release and publish to npm
release-and-publish:
needs: [prepare, check-artifacts]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.prepare.outputs.tag }}
- uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
- uses: pnpm/action-setup@v4
with:
version: 9
- name: Download CI artifacts
uses: dawidd6/action-download-artifact@v6
with:
workflow: ci.yml
run_id: ${{ needs.check-artifacts.outputs.run_id }}
path: artifacts
name: ffmpeg-*
name_is_regexp: true
- name: Flatten and verify artifacts
run: |
cd artifacts
# Flatten subdirectories
for dir in */; do
[[ -d "$dir" ]] && mv "$dir"* . 2>/dev/null && rmdir "$dir" 2>/dev/null || true
done
# Verify count
shopt -s nullglob
tarballs=(*.tar.gz)
if [[ ${#tarballs[@]} -ne 8 ]]; then
echo "::error::Expected 8 tarballs, found ${#tarballs[@]}"
ls -la
exit 1
fi
# Verify checksums
for f in *.sha256; do
sha256sum --check "$f"
done
echo "All 8 artifacts verified"
- name: Generate attestations
uses: actions/attest-build-provenance@v2
with:
subject-path: 'artifacts/*.tar.gz'
- name: Create GitHub release
uses: ncipollo/release-action@v1
with:
tag: ${{ needs.prepare.outputs.tag }}
commit: ${{ needs.prepare.outputs.commit }}
artifacts: "artifacts/*.tar.gz,artifacts/*.sha256"
generateReleaseNotes: true
allowUpdates: true
- name: Extract and populate npm packages
run: |
# Extract tarballs - each creates <platform>-<tier>/ directory
mkdir -p extracted
for tarball in artifacts/*.tar.gz; do
tar -xzf "$tarball" -C extracted
done
# Move to artifacts directory for populate-artifacts.sh
mv extracted artifacts-extracted
rm -rf artifacts
mv artifacts-extracted artifacts
# populate-artifacts.sh copies lib/include only, doesn't touch package.json
./scripts/populate-artifacts.sh
- run: cd npm && pnpm publish -r --provenance --access public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}