Release Packages #83
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release Packages | |
| on: | |
| workflow_run: | |
| workflows: ["Tag on Merge"] | |
| types: [completed] | |
| permissions: | |
| id-token: write | |
| contents: write | |
| env: | |
| CI: 1 | |
| UPDATE: 1 | |
| jobs: | |
| publish: | |
| environment: npm-release | |
| runs-on: ubuntu-latest | |
| env: | |
| tags: "" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Fetch all tags | |
| run: git fetch --tags | |
| - name: Find all new package tags on this commit | |
| id: find_tags | |
| shell: bash | |
| run: | | |
| TAGS=$(git tag --points-at HEAD | grep -E '^@instructure.ai/.+@([0-9]+\.[0-9]+\.[0-9]+)$' || true) | |
| # Output for conditionals in later steps | |
| echo "tags<<EOF" >> "$GITHUB_OUTPUT" | |
| echo "$TAGS" >> "$GITHUB_OUTPUT" | |
| echo "EOF" >> "$GITHUB_OUTPUT" | |
| # Also set env var for shell scripts in later steps | |
| { | |
| echo "tags<<EOF" | |
| echo "$TAGS" | |
| echo "EOF" | |
| } >> "$GITHUB_ENV" | |
| if [[ -z "$TAGS" ]]; then | |
| echo "No new matching tags found on this commit." | |
| fi | |
| # Only set up Node/pnpm + caching if we actually have tags to release | |
| - name: Install pnpm | |
| if: steps.find_tags.outputs.tags != '' | |
| run: npm install -g pnpm | |
| - uses: actions/setup-node@v4 | |
| if: steps.find_tags.outputs.tags != '' | |
| with: | |
| node-version: '24' | |
| registry-url: 'https://registry.npmjs.org' | |
| cache: 'pnpm' | |
| cache-dependency-path: | | |
| pnpm-lock.yaml | |
| **/pnpm-lock.yaml | |
| # Pre-create the ACTUAL pnpm store so post-job cache save never errors | |
| - name: Ensure pnpm store exists for caching | |
| if: steps.find_tags.outputs.tags != '' | |
| shell: bash | |
| run: | | |
| STORE="$(pnpm store path)" | |
| echo "pnpm store path: $STORE" | |
| mkdir -p "$STORE" | |
| - name: Release each package tag | |
| if: steps.find_tags.outputs.tags != '' | |
| env: | |
| tags: ${{ env.tags }} # comes from $GITHUB_ENV we set above | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| IFS=$'\n' | |
| for TAG in $tags; do | |
| PKG_NAME=$(echo "$TAG" | sed -E 's/@instructure.ai\/([^@]+)@.*/\1/') | |
| echo "Processing release for tag: $TAG (package: $PKG_NAME)" | |
| # Remove deprecated always-auth from any pre-existing .npmrc | |
| if [ -f "$HOME/.npmrc" ]; then | |
| sed -i.bak '/always-auth/d' "$HOME/.npmrc" | |
| fi | |
| # Install deps | |
| pnpm install --frozen-lockfile | |
| # Enable vite-node loader using the register() API | |
| export NODE_OPTIONS="--import=${{ github.workspace }}/plugins/vite-node.plugin.loader.mjs" | |
| # Only build & test if package is in /packages (skip apps and shared-configs) | |
| if [ -d "packages/$PKG_NAME" ] && [ "$PKG_NAME" != "shared-configs" ]; then | |
| echo "Building package: $PKG_NAME (in /packages)" | |
| pnpm test package "$PKG_NAME" | |
| pnpm build package "$PKG_NAME" | |
| else | |
| echo "Skipping build/test for $PKG_NAME (not in /packages or is shared-configs)." | |
| fi | |
| # Publish (only for /packages, skip apps and shared-configs) | |
| if [ -d "packages/$PKG_NAME" ] && [ "$PKG_NAME" != "shared-configs" ]; then | |
| PKG_JSON_PATH="packages/$PKG_NAME/package.json" | |
| if [ -f "$PKG_JSON_PATH" ]; then | |
| JSON_PATH="$PKG_JSON_PATH" | |
| else | |
| JSON_PATH="" | |
| fi | |
| TARBALL=$(find "./packages/$PKG_NAME/src" -name '*.tgz' -type f | head -n 1) | |
| if [ -z "$TARBALL" ]; then | |
| echo "Error: No .tgz tarball found for $PKG_NAME in ./packages/$PKG_NAME/src/" | |
| continue | |
| fi | |
| if [ -n "$JSON_PATH" ]; then | |
| ACCESS_PUBLIC=$(jq -r '.publishConfig.access // empty' "$JSON_PATH") | |
| PRIVATE=$(jq -r '.private // false' "$JSON_PATH") | |
| if [ "$PRIVATE" = "true" ]; then | |
| echo "Skipping publish for $PKG_NAME because it is marked private." | |
| elif [ "$ACCESS_PUBLIC" = "public" ]; then | |
| echo "Publishing $PKG_NAME as public via npm (OIDC)…" | |
| npm publish "$TARBALL" --access public --provenance | |
| elif [ -n "$ACCESS_PUBLIC" ] && [ "$ACCESS_PUBLIC" != "public" ]; then | |
| echo "Skipping publish for $PKG_NAME because publishConfig.access is '$ACCESS_PUBLIC'." | |
| else | |
| echo "Publishing $PKG_NAME via npm (no --access public)…" | |
| npm publish "$TARBALL" --provenance | |
| fi | |
| else | |
| echo "package.json not found for $PKG_NAME in packages, publishing without access check." | |
| npm publish "$TARBALL" --provenance | |
| fi | |
| else | |
| echo "Skipping publish for $PKG_NAME (not in /packages or is shared-configs)." | |
| fi | |
| # Create GitHub release (attach asset only for /packages) | |
| if [ "$PKG_NAME" = "shared-configs" ]; then | |
| gh release create "$TAG" --title "$TAG" --generate-notes | |
| elif [ -d "packages/$PKG_NAME" ]; then | |
| gh release create "$TAG" ./packages/$PKG_NAME/src/*.tgz --title "$TAG" --generate-notes | |
| else | |
| gh release create "$TAG" --title "$TAG" --generate-notes | |
| fi | |
| done | |
| - name: Trigger nutritionfacts auto-release | |
| if: steps.find_tags.outputs.tags != '' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TAGS: ${{ steps.find_tags.outputs.tags }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| IFS=$'\n' | |
| for TAG in $TAGS; do | |
| # Extract the package name from the tag (e.g., @instructure.ai/[email protected] => foo) | |
| NAME=$(echo "$TAG" | sed -E 's/@instructure.ai\/([^@]+)@.*/\1/') | |
| EVENT_TYPE="${NAME}_release" | |
| # Build the required JSON payload and POST it | |
| PAYLOAD=$(jq -n \ | |
| --arg et "$EVENT_TYPE" \ | |
| --arg tag "$TAG" \ | |
| '{event_type: $et, client_payload: {tag: $tag}}') | |
| echo "Dispatching event: $EVENT_TYPE with payload: $PAYLOAD" | |
| echo "$PAYLOAD" | gh api repos/${{ github.repository }}/dispatches \ | |
| --method POST \ | |
| -H "Accept: application/vnd.github+json" \ | |
| --input - | |
| done |