Publish to Public NPM #46
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: Publish to Public NPM | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| core_version: | |
| description: "Core package version (leave empty to use package.json version)" | |
| required: false | |
| type: string | |
| react_version: | |
| description: "React package version (leave empty to use package.json version)" | |
| required: false | |
| type: string | |
| packages: | |
| description: "Packages to publish" | |
| required: true | |
| type: choice | |
| default: "both" | |
| options: | |
| - "both" | |
| - "core" | |
| - "react" | |
| npm_tag: | |
| description: "NPM dist-tag (auto = detect from version)" | |
| required: false | |
| type: choice | |
| default: "latest" | |
| options: | |
| - "auto" | |
| - "latest" | |
| - "beta" | |
| - "alpha" | |
| - "next" | |
| dry_run: | |
| description: "Dry run (preview without publishing)" | |
| required: false | |
| type: boolean | |
| default: true | |
| skip_slack_notification: | |
| description: "Skip Slack notification" | |
| required: false | |
| type: boolean | |
| default: false | |
| permissions: | |
| contents: write | |
| id-token: write | |
| jobs: | |
| publish: | |
| name: Build & Publish to Public NPM | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| id-token: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 | |
| with: | |
| version: 10.6.5 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: "20" | |
| cache: "pnpm" | |
| registry-url: "https://registry.npmjs.org" | |
| - name: Upgrade npm to latest version | |
| run: npm install -g npm@latest | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build packages | |
| run: pnpm run build | |
| - name: Detect versions and npm tags | |
| id: detect | |
| run: | | |
| CORE_VERSION=$(node -p "require('./packages/core/package.json').version") | |
| REACT_VERSION=$(node -p "require('./packages/react/package.json').version") | |
| determine_tag() { | |
| local version=$1 | |
| if [[ "$version" == *"alpha"* ]]; then | |
| echo "alpha" | |
| elif [[ "$version" == *"beta"* ]]; then | |
| echo "beta" | |
| elif [[ "$version" == *"rc"* ]]; then | |
| echo "next" | |
| else | |
| echo "latest" | |
| fi | |
| } | |
| CORE_TAG=$(determine_tag "$CORE_VERSION") | |
| REACT_TAG=$(determine_tag "$REACT_VERSION") | |
| echo "Detected package versions and tags from package:" | |
| echo "Core Version: $CORE_VERSION" | |
| echo "React Version: $REACT_VERSION" | |
| echo "Core Tag: ${CORE_TAG}" | |
| echo "React Tag: ${REACT_TAG}" | |
| echo "Setting environment variables: core_version, react_version, core_npm_tag & react_npm_tag" | |
| echo "core_version=${CORE_VERSION}" >> $GITHUB_ENV | |
| echo "react_version=${REACT_VERSION}" >> $GITHUB_ENV | |
| echo "core_npm_tag=${CORE_TAG}" >> $GITHUB_ENV | |
| echo "react_npm_tag=${REACT_TAG}" >> $GITHUB_ENV | |
| - name: Set finalized versions, tags and packages to publish | |
| id: final | |
| env: | |
| CORE_VER: ${{ github.event.inputs.core_version }} | |
| REACT_VER: ${{ github.event.inputs.react_version }} | |
| NPM_TAG_INPUT: ${{ github.event.inputs.npm_tag }} | |
| PACKAGES_INPUT: ${{ github.event.inputs.packages }} | |
| run: | | |
| # Validation(Sanitize) function, exit on invalid input | |
| validate_input() { | |
| local input="$1" | |
| # Allow empty input | |
| if [[ -z "$input" ]]; then | |
| echo "" | |
| return 0 | |
| fi | |
| if [[ "$input" =~ ^[a-zA-Z0-9._-]+$ ]]; then | |
| echo "$input" | |
| else | |
| echo "Invalid input: $input" >&2 | |
| exit 1 | |
| fi | |
| } | |
| # Validate user inputs for malicious content, exit if invalid | |
| validate_input "$CORE_VER" || exit 1 | |
| validate_input "$REACT_VER" || exit 1 | |
| validate_input "$NPM_TAG_INPUT" || exit 1 | |
| validate_input "$PACKAGES_INPUT" || exit 1 | |
| if [ -z "$CORE_VER" ]; then | |
| CORE_VER="$core_version" | |
| fi | |
| if [ -z "$REACT_VER" ]; then | |
| REACT_VER="$react_version" | |
| fi | |
| if [ "$NPM_TAG_INPUT" = "auto" ] || [ -z "$NPM_TAG_INPUT" ]; then | |
| CORE_TAG="$core_npm_tag" | |
| REACT_TAG="$react_npm_tag" | |
| else | |
| CORE_TAG="$NPM_TAG_INPUT" | |
| REACT_TAG="$NPM_TAG_INPUT" | |
| fi | |
| # Assign final tag values into environment variables | |
| echo "CORE_VER=${CORE_VER}" >> $GITHUB_ENV | |
| echo "REACT_VER=${REACT_VER}" >> $GITHUB_ENV | |
| echo "CORE_TAG=${CORE_TAG}" >> $GITHUB_ENV | |
| echo "REACT_TAG=${REACT_TAG}" >> $GITHUB_ENV | |
| echo "PACKAGES_INPUT=${PACKAGES_INPUT}" >> $GITHUB_ENV | |
| echo "Publishing:" | |
| echo "Core: ${CORE_VER} @ ${CORE_TAG}" | |
| echo "React: ${REACT_VER} @ ${REACT_TAG}" | |
| echo "Packages: ${PACKAGES_INPUT}" | |
| - name: Update Core package for public npm | |
| if: | | |
| (env.PACKAGES_INPUT == 'both' || env.PACKAGES_INPUT == 'core') | |
| working-directory: ./packages/core | |
| run: | | |
| node -e " | |
| const pkg = require('./package.json'); | |
| pkg.version = process.env.CORE_VER; | |
| pkg.publishConfig = { | |
| access: 'public', | |
| registry: 'https://registry.npmjs.org/' | |
| }; | |
| require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n'); | |
| " | |
| - name: Update React package for public npm | |
| if: | | |
| (env.PACKAGES_INPUT == 'both' || env.PACKAGES_INPUT == 'react') | |
| working-directory: ./packages/react | |
| run: | | |
| node -e " | |
| const pkg = require('./package.json'); | |
| pkg.version = process.env.REACT_VER; | |
| pkg.publishConfig = { | |
| access: 'public', | |
| registry: 'https://registry.npmjs.org/' | |
| }; | |
| if (pkg.dependencies && pkg.dependencies['@auth0/universal-components-core']) { | |
| pkg.dependencies['@auth0/universal-components-core'] = process.env.CORE_VER; | |
| } | |
| require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n'); | |
| " | |
| - name: Publish Core package to public npm | |
| id: publish-core | |
| if: | | |
| (env.PACKAGES_INPUT == 'both' || env.PACKAGES_INPUT == 'core') | |
| working-directory: ./packages/core | |
| env: | |
| DRY_RUN: ${{ github.event.inputs.dry_run }} | |
| run: | | |
| current_version=$(node -p "require('./package.json').version") | |
| echo "Publishing @auth0/universal-components-core@$current_version with tag: $CORE_TAG" | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo "DRY RUN - Skipping publish" | |
| npm publish --dry-run --tag="$CORE_TAG" | |
| echo "published=dry-run" >> $GITHUB_OUTPUT | |
| echo "version=$current_version" >> $GITHUB_OUTPUT | |
| echo "Published @auth0/universal-components-core@$current_version (dry run)" | |
| else | |
| npm publish --provenance --access public --tag="$CORE_TAG" | |
| echo "published=true" >> $GITHUB_OUTPUT | |
| echo "version=$current_version" >> $GITHUB_OUTPUT | |
| echo "Published @auth0/universal-components-core@$current_version" | |
| fi | |
| - name: Publish React package to public npm | |
| id: publish-react | |
| if: | | |
| (env.PACKAGES_INPUT == 'both' || env.PACKAGES_INPUT == 'react') | |
| working-directory: ./packages/react | |
| env: | |
| DRY_RUN: ${{ github.event.inputs.dry_run }} | |
| run: | | |
| current_version=$(node -p "require('./package.json').version") | |
| echo "Publishing @auth0/universal-components-react@$current_version with tag: $REACT_TAG" | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo "DRY RUN - Skipping publish" | |
| npm publish --dry-run --tag="$REACT_TAG" | |
| echo "published=dry-run" >> $GITHUB_OUTPUT | |
| echo "version=$current_version" >> $GITHUB_OUTPUT | |
| else | |
| npm publish --provenance --access public --tag="$REACT_TAG" | |
| echo "published=true" >> $GITHUB_OUTPUT | |
| echo "version=$current_version" >> $GITHUB_OUTPUT | |
| echo "Published @auth0/universal-components-react@$current_version" | |
| fi | |
| - name: Generate Summary | |
| env: | |
| DRY_RUN: ${{ github.event.inputs.dry_run }} | |
| run: | | |
| echo "## Public NPM Publication Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo "### DRY RUN MODE" >> $GITHUB_STEP_SUMMARY | |
| echo "No packages were published. This was a test run." >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "### Published Packages" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ steps.publish-core.outputs.published }}" != "" ]; then | |
| echo "- \`@auth0/universal-components-core@${CORE_VER}\`" >> $GITHUB_STEP_SUMMARY | |
| echo " - Tag: \`${CORE_TAG}\`" >> $GITHUB_STEP_SUMMARY | |
| echo " - Registry: https://registry.npmjs.org" >> $GITHUB_STEP_SUMMARY | |
| echo " - Install: \`npm install @auth0/universal-components-core@${CORE_TAG}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ steps.publish-react.outputs.published }}" != "" ]; then | |
| echo "- \`@auth0/universal-components-react@${REACT_VER}\`" >> $GITHUB_STEP_SUMMARY | |
| echo " - Tag: \`${REACT_TAG}\`" >> $GITHUB_STEP_SUMMARY | |
| echo " - Registry: https://registry.npmjs.org" >> $GITHUB_STEP_SUMMARY | |
| echo " - Install: \`npm install @auth0/universal-components-react@${REACT_TAG}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "### Links" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ steps.publish-core.outputs.published }}" != "" ]; then | |
| echo "- [View @auth0/universal-components-core on npm](https://www.npmjs.com/package/@auth0/universal-components-core)" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ steps.publish-react.outputs.published }}" != "" ]; then | |
| echo "- [View @auth0/universal-components-react on npm](https://www.npmjs.com/package/@auth0/universal-components-react)" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "---" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Note:** Your internal JFrog registry continues to use \`@auth0-web-ui-components/*\` packages." >> $GITHUB_STEP_SUMMARY | |
| - name: Create Git tags | |
| if: github.event.inputs.dry_run != 'true' | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| if [ "${{ steps.publish-core.outputs.published }}" = "true" ]; then | |
| TAG_NAME="core@$CORE_VER" | |
| # Check if tag exists locally and delete it | |
| if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then | |
| echo "Tag $TAG_NAME exists locally, deleting..." | |
| git tag -d "$TAG_NAME" | |
| fi | |
| # Check if tag exists remotely and delete it | |
| if git ls-remote --tags origin | grep -q "refs/tags/$TAG_NAME"; then | |
| echo "Tag $TAG_NAME exists remotely, deleting..." | |
| git push origin ":refs/tags/$TAG_NAME" || true | |
| fi | |
| git tag -a "$TAG_NAME" -m "Release @auth0/universal-components-core@$CORE_VER" | |
| git push origin "$TAG_NAME" | |
| echo "Created tag: $TAG_NAME" | |
| fi | |
| if [ "${{ steps.publish-react.outputs.published }}" = "true" ]; then | |
| TAG_NAME="react@$REACT_VER" | |
| # Check if tag exists locally and delete it | |
| if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then | |
| echo "Tag $TAG_NAME exists locally, deleting..." | |
| git tag -d "$TAG_NAME" | |
| fi | |
| # Check if tag exists remotely and delete it | |
| if git ls-remote --tags origin | grep -q "refs/tags/$TAG_NAME"; then | |
| echo "Tag $TAG_NAME exists remotely, deleting..." | |
| git push origin ":refs/tags/$TAG_NAME" || true | |
| fi | |
| git tag -a "$TAG_NAME" -m "Release @auth0/universal-components-react@$REACT_VER" | |
| git push origin "$TAG_NAME" | |
| echo "Created tag: $TAG_NAME" | |
| fi | |
| - name: Create GitHub Release for Core | |
| if: | | |
| github.event.inputs.dry_run != 'true' && | |
| steps.publish-core.outputs.published == 'true' | |
| uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2 | |
| with: | |
| tag_name: core@${{ env.CORE_VER }} | |
| name: "@auth0/universal-components-core@${{ env.CORE_VER }}" | |
| body: | | |
| Published to NPM | |
| **Package**: `@auth0/universal-components-core` | |
| **Version**: `${{ env.CORE_VER }}` | |
| **NPM Tag**: `${{ env.CORE_TAG }}` | |
| ```bash | |
| npm install @auth0/universal-components-core@${{ env.CORE_VER }} | |
| ``` | |
| [View on NPM](https://www.npmjs.com/package/@auth0/universal-components-core/v/${{ env.CORE_VER }}) | |
| prerelease: ${{ env.CORE_TAG != 'latest' }} | |
| draft: false | |
| - name: Create GitHub Release for React | |
| if: | | |
| github.event.inputs.dry_run != 'true' && | |
| steps.publish-react.outputs.published == 'true' | |
| uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2 | |
| with: | |
| tag_name: react@${{ env.REACT_VER }} | |
| name: "@auth0/universal-components-react@${{ env.REACT_VER }}" | |
| body: | | |
| Published to NPM | |
| **Package**: `@auth0/universal-components-react` | |
| **Version**: `${{ env.REACT_VER }}` | |
| **NPM Tag**: `${{ env.REACT_TAG }}` | |
| **Requires**: `@auth0/universal-components-core@${{ env.CORE_VER }}` | |
| ```bash | |
| npm install @auth0/universal-components-react@${{ env.REACT_VER }} | |
| ``` | |
| [View on NPM](https://www.npmjs.com/package/@auth0/universal-components-react/v/${{ env.REACT_VER }}) | |
| prerelease: ${{ env.REACT_TAG != 'latest' }} | |
| draft: false | |
| - name: Restore package.json files | |
| if: always() | |
| run: | | |
| git checkout packages/core/package.json packages/react/package.json || true | |
| - name: Send Slack notification | |
| if: | | |
| always() && | |
| !cancelled() && | |
| github.event.inputs.dry_run != 'true' && | |
| github.event.inputs.skip_slack_notification != 'true' | |
| env: | |
| DRY_RUN: ${{ github.event.inputs.dry_run }} | |
| SLACK_WORKFLOW_WEBHOOK: ${{ secrets.SLACK_WORKFLOW_WEBHOOK }} | |
| run: | | |
| echo "Preparing Slack notification..." | |
| # Determine what was actually published | |
| PUBLISHED_PACKAGES="" | |
| PUBLISH_ERRORS="" | |
| # Check core package publishing | |
| if [ "${{ steps.publish-core.outputs.published }}" = "true" ]; then | |
| PUBLISHED_PACKAGES="@auth0/universal-components-core@${{ steps.publish-core.outputs.version }}" | |
| elif [ "${{ steps.publish-core.outputs.published }}" = "dry-run" ]; then | |
| PUBLISHED_PACKAGES="@auth0/universal-components-core@${{ steps.publish-core.outputs.version }} (dry run)" | |
| elif [ "${{ steps.publish-core.outcome }}" = "failure" ]; then | |
| PUBLISH_ERRORS="core package failed to publish" | |
| fi | |
| # Check react package publishing | |
| if [ "${{ steps.publish-react.outputs.published }}" = "true" ]; then | |
| if [ -n "$PUBLISHED_PACKAGES" ]; then | |
| PUBLISHED_PACKAGES="$PUBLISHED_PACKAGES, @auth0/universal-components-react@${{ steps.publish-react.outputs.version }}" | |
| else | |
| PUBLISHED_PACKAGES="@auth0/universal-components-react@${{ steps.publish-react.outputs.version }}" | |
| fi | |
| elif [ "${{ steps.publish-react.outputs.published }}" = "dry-run" ]; then | |
| if [ -n "$PUBLISHED_PACKAGES" ]; then | |
| PUBLISHED_PACKAGES="$PUBLISHED_PACKAGES, @auth0/universal-components-react@${{ steps.publish-react.outputs.version }} (dry run)" | |
| else | |
| PUBLISHED_PACKAGES="@auth0/universal-components-react@${{ steps.publish-react.outputs.version }} (dry run)" | |
| fi | |
| elif [ "${{ steps.publish-react.outcome }}" = "failure" ]; then | |
| if [ -n "$PUBLISH_ERRORS" ]; then | |
| PUBLISH_ERRORS="$PUBLISH_ERRORS, react package failed to publish" | |
| else | |
| PUBLISH_ERRORS="react package failed to publish" | |
| fi | |
| fi | |
| # Determine overall status and message | |
| if [ -n "$PUBLISHED_PACKAGES" ] && [ -z "$PUBLISH_ERRORS" ]; then | |
| STATUS="SUCCESS: Packages published to public NPM" | |
| MESSAGE="Published: $PUBLISHED_PACKAGES" | |
| elif [ -n "$PUBLISHED_PACKAGES" ] && [ -n "$PUBLISH_ERRORS" ]; then | |
| STATUS="WARNING: Partial publishing failure" | |
| MESSAGE="Published: $PUBLISHED_PACKAGES. Errors: $PUBLISH_ERRORS" | |
| elif [ -n "$PUBLISH_ERRORS" ]; then | |
| STATUS="FAILED: Package publishing failed" | |
| MESSAGE="Errors: $PUBLISH_ERRORS" | |
| else | |
| STATUS="INFO: No packages published" | |
| MESSAGE="No packages were selected for publishing" | |
| fi | |
| # Get individual package versions for Slack | |
| CORE_VERSION="Not published" | |
| REACT_VERSION="Not published" | |
| if [ "${{ steps.publish-core.outputs.published }}" = "true" ] || [ "${{ steps.publish-core.outputs.published }}" = "dry-run" ]; then | |
| CORE_VERSION="${{ steps.publish-core.outputs.version }}" | |
| fi | |
| if [ "${{ steps.publish-react.outputs.published }}" = "true" ] || [ "${{ steps.publish-react.outputs.published }}" = "dry-run" ]; then | |
| REACT_VERSION="${{ steps.publish-react.outputs.version }}" | |
| fi | |
| # Output summary for GitHub logs | |
| echo "=== WORKFLOW SUMMARY ===" | |
| echo "Status: $STATUS" | |
| echo "Message: $MESSAGE" | |
| echo "" | |
| # Send notification to Slack (if webhook is configured) | |
| if [ -n "$SLACK_WORKFLOW_WEBHOOK" ]; then | |
| echo "Sending Slack notification..." | |
| HTTP_CODE=$(curl -X POST "$SLACK_WORKFLOW_WEBHOOK" \ | |
| -H "Content-Type: application/json" \ | |
| -w "%{http_code}" \ | |
| -o /tmp/slack_response.txt \ | |
| -d "{ | |
| \"status\": \"$STATUS\", | |
| \"message\": \"$MESSAGE\", | |
| \"package_details\": \"${PUBLISHED_PACKAGES:-'No packages published'}\", | |
| \"core_version\": \"$CORE_VERSION\", | |
| \"react_version\": \"$REACT_VERSION\", | |
| \"registry\": \"https://registry.npmjs.org\", | |
| \"trigger\": \"${{ github.event_name }}\", | |
| \"actor\": \"${{ github.actor }}\", | |
| \"action_url\": \"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\" | |
| }") | |
| if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then | |
| echo "Slack notification sent successfully (HTTP $HTTP_CODE)" | |
| else | |
| echo "WARNING: Slack notification failed (HTTP $HTTP_CODE)" | |
| cat /tmp/slack_response.txt | |
| fi | |
| else | |
| echo "Slack webhook not configured, skipping notification" | |
| fi | |
| - name: Handle workflow failure | |
| if: failure() | |
| run: | | |
| echo "## Workflow Failed" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "The workflow encountered an error. Please check the logs above for details." >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Common Issues" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Authentication**: Verify NPM_TOKEN secret is configured correctly" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Version conflict**: Package version may already exist on npm" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Build failure**: Check if packages build successfully" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Network issues**: NPM registry may be unreachable" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Next Steps" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "1. Review the error logs in this workflow run" >> $GITHUB_STEP_SUMMARY | |
| echo "2. Fix the issue and retry the workflow" >> $GITHUB_STEP_SUMMARY | |
| echo "3. If needed, manually publish using: \`pnpm publish\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Workflow Run:** ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> $GITHUB_STEP_SUMMARY |