perf(component): render initial diff during SSR #144
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
| # 1. Workflow metadata | |
| name: Publish to NPM | |
| permissions: | |
| contents: read | |
| packages: read | |
| pull-requests: read | |
| # 2. Trigger conditions | |
| on: | |
| pull_request: | |
| types: [closed] | |
| branches: | |
| - main | |
| - '!dependabot/**' | |
| paths: | |
| - src/** | |
| - '!docs/**' | |
| - '!tests/**' | |
| - '!src/lib/test/**' | |
| - '!src/routes/**' | |
| - package.json | |
| - pnpm-lock.yaml | |
| - .github/workflows/npm-publish.yml | |
| workflow_dispatch: | |
| inputs: | |
| # trunk-ignore(checkov/CKV_GHA_7): We need manual version control for releases | |
| version_bump: | |
| description: Version bump type (major/minor/patch) or skip | |
| required: true | |
| type: choice | |
| options: | |
| - skip | |
| - patch | |
| - minor | |
| - major | |
| custom_message: | |
| description: Release message | |
| required: false | |
| type: string | |
| jobs: | |
| check-if-merged: | |
| runs-on: blacksmith-2vcpu-ubuntu-2404 # trunk-ignore(actionlint/runner-label) | |
| outputs: | |
| should_run: ${{ steps.check.outputs.should_run }} | |
| has_skip_label: ${{ steps.check.outputs.has_skip_label }} | |
| steps: | |
| - id: check | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| PR_MERGED: ${{ github.event.pull_request.merged }} | |
| HAS_SKIP_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'skip-publish') }} | |
| run: | | |
| if [[ "$EVENT_NAME" == "pull_request" && "$PR_MERGED" != "true" ]]; then | |
| echo "should_run=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "should_run=true" >> $GITHUB_OUTPUT | |
| fi | |
| echo "has_skip_label=$HAS_SKIP_LABEL" >> $GITHUB_OUTPUT | |
| debug-check: | |
| needs: check-if-merged | |
| if: needs.check-if-merged.outputs.should_run == 'true' | |
| runs-on: blacksmith-2vcpu-ubuntu-2404 # trunk-ignore(actionlint/runner-label) | |
| environment: ci | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 # zizmor: ignore[unpinned-uses] | |
| with: | |
| persist-credentials: false | |
| token: ${{ secrets.ACTIONS_KEY }} | |
| - name: Check for debugging statements | |
| run: | | |
| # Function to check file content | |
| check_file() { | |
| local file="$1" | |
| local has_debug=false | |
| local debug_output="" | |
| # Check for $inspect (not in comments) | |
| if grep -n -P '^(?!\s*//|\s*/\*|\s*\*).*\$inspect' "$file" > /dev/null; then | |
| match=$(grep -n -P '^(?!\s*//|\s*/\*|\s*\*).*\$inspect.*' "$file" | head -n 1) | |
| line_num=$(echo "$match" | cut -d: -f1) | |
| statement=$(echo "$match" | cut -d: -f2-) | |
| echo "::error file=$file::Found \$inspect statement on line $line_num: $statement" | |
| has_debug=true | |
| fi | |
| # Check for console.log (not in comments) | |
| if grep -n -P '^(?!\s*//|\s*/\*|\s*\*).*console\.log' "$file" > /dev/null; then | |
| match=$(grep -n -P '^(?!\s*//|\s*/\*|\s*\*).*console\.log.*' "$file" | head -n 1) | |
| line_num=$(echo "$match" | cut -d: -f1) | |
| statement=$(echo "$match" | cut -d: -f2-) | |
| echo "::error file=$file::Found console.log statement on line $line_num: $statement" | |
| has_debug=true | |
| fi | |
| # Check for console.debug (not in comments) | |
| if grep -n -P '^(?!\s*//|\s*/\*|\s*\*).*console\.debug' "$file" > /dev/null; then | |
| match=$(grep -n -P '^(?!\s*//|\s*/\*|\s*\*).*console\.debug.*' "$file" | head -n 1) | |
| line_num=$(echo "$match" | cut -d: -f1) | |
| statement=$(echo "$match" | cut -d: -f2-) | |
| echo "::error file=$file::Found console.debug statement on line $line_num: $statement" | |
| has_debug=true | |
| fi | |
| # Check for debugger statements (not in comments) | |
| if grep -n -P '^(?!\s*//|\s*/\*|\s*\*).*debugger;' "$file" > /dev/null; then | |
| match=$(grep -n -P '^(?!\s*//|\s*/\*|\s*\*).*debugger;' "$file" | head -n 1) | |
| line_num=$(echo "$match" | cut -d: -f1) | |
| statement=$(echo "$match" | cut -d: -f2-) | |
| echo "::error file=$file::Found debugger statement on line $line_num: $statement" | |
| has_debug=true | |
| fi | |
| $has_debug && return 1 || return 0 | |
| } | |
| # Set up file descriptor for output capture | |
| exec 5>&1 | |
| # Run checks and capture output | |
| OUTPUT=$( | |
| { | |
| has_errors=0 | |
| # Check Svelte files | |
| while IFS= read -r file; do | |
| if ! check_file "$file"; then | |
| has_errors=1 | |
| fi | |
| done < <(find src/lib -type f -name "*.svelte" ! -path "*/test/*") | |
| # Check TypeScript files (excluding log.ts which intentionally uses console.log) | |
| while IFS= read -r file; do | |
| if ! check_file "$file"; then | |
| has_errors=1 | |
| fi | |
| done < <(find src/lib -type f -name "*.ts" ! -name "*.test.ts" ! -path "*/test/*" ! -path "src/lib/utils/log.ts") | |
| exit $has_errors | |
| } | tee >(cat >&5) | |
| ) | |
| # Store output for the next step | |
| { | |
| echo "DEBUG_OUTPUT<<EOF" | |
| echo "$OUTPUT" | |
| echo "EOF" | |
| } >> $GITHUB_ENV | |
| # Exit with the original error code | |
| [[ $OUTPUT == *"::error"* ]] && exit 1 || exit 0 | |
| - name: Upload Results | |
| if: failure() && github.event_name == 'pull_request' | |
| uses: actions/github-script@v9 # zizmor: ignore[unpinned-uses] | |
| with: | |
| github-token: ${{ secrets.ACTIONS_KEY }} | |
| script: | | |
| const fs = require('fs'); | |
| const { owner, repo } = context.repo; | |
| // Get the debug check output | |
| const debugOutput = process.env.DEBUG_OUTPUT || ''; | |
| let commentBody = '❌ Debug statements check failed.\n\nFound debugging statements in the following locations:\n\n'; | |
| // Add each error location to the comment | |
| debugOutput.split('\n').forEach(line => { | |
| if (line.includes('::error file=')) { | |
| const match = line.match(/::error file=(.+?)::(.+)/); | |
| if (match) { | |
| const [_, file, message] = match; | |
| commentBody += `- ${file}: ${message}\n`; | |
| } | |
| } | |
| }); | |
| commentBody += '\nPlease remove these debugging statements before merging.'; | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: owner, | |
| repo: repo, | |
| body: commentBody | |
| }); | |
| - name: Show Results | |
| if: failure() && github.event_name == 'workflow_dispatch' | |
| run: | | |
| echo "::group::Debug Check Results" | |
| echo "$DEBUG_OUTPUT" | |
| echo "::endgroup::" | |
| echo "❌ Debug statements were found. Please remove them before proceeding." | |
| exit 1 | |
| # 3. Testing jobs (grouped together) | |
| build: | |
| needs: [check-if-merged, debug-check] | |
| if: needs.check-if-merged.outputs.should_run == 'true' | |
| runs-on: blacksmith-2vcpu-ubuntu-2404 # trunk-ignore(actionlint/runner-label) | |
| environment: ci | |
| strategy: | |
| matrix: | |
| node-version: [22, 24] | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 # zizmor: ignore[unpinned-uses] | |
| with: | |
| persist-credentials: false | |
| token: ${{ secrets.ACTIONS_KEY }} | |
| - uses: pnpm/action-setup@v6 # zizmor: ignore[unpinned-uses] | |
| - name: Use Node.js ${{ matrix.node-version }} | |
| uses: actions/setup-node@v7 # zizmor: ignore[unpinned-uses] | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| - name: Install | |
| run: | | |
| pnpm install --filter . --prefer-offline --frozen-lockfile --ignore-scripts --config.engine-strict=false | |
| - name: Test | |
| run: | | |
| pnpm build | |
| pnpm test | |
| - name: Upload coverage to Coveralls | |
| uses: coverallsapp/github-action@v2 # zizmor: ignore[unpinned-uses] | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| flag-name: node-${{ matrix.node-version }} | |
| parallel: true | |
| fail-on-error: false | |
| if: matrix.node-version == '22' | |
| # Removed dependency caching to reduce cache-poisoning risk | |
| playwright-tests: | |
| needs: [check-if-merged, debug-check] | |
| if: needs.check-if-merged.outputs.should_run == 'true' && needs.check-if-merged.outputs.has_skip_label != 'true' | |
| timeout-minutes: 60 | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 # trunk-ignore(actionlint/runner-label) | |
| environment: ci | |
| steps: | |
| - uses: actions/checkout@v7 # zizmor: ignore[unpinned-uses] | |
| with: | |
| persist-credentials: false | |
| token: ${{ secrets.ACTIONS_KEY }} | |
| - uses: pnpm/action-setup@v6 # zizmor: ignore[unpinned-uses] | |
| - uses: actions/setup-node@v7 # zizmor: ignore[unpinned-uses] | |
| with: | |
| node-version: 24 | |
| - name: Install dependencies | |
| run: pnpm install --filter . --prefer-offline --frozen-lockfile --ignore-scripts --config.engine-strict=false | |
| - name: Install Playwright Browsers | |
| run: pnpm exec playwright install --with-deps | |
| - name: Run Playwright tests | |
| run: pnpm run test:e2e | |
| - uses: actions/upload-artifact@v7 # zizmor: ignore[unpinned-uses] | |
| if: always() | |
| with: | |
| name: playwright-results | |
| path: test-results/ | |
| retention-days: 10 | |
| # 4. Coverage reporting (depends on tests) | |
| coverage-report: | |
| needs: [check-if-merged, build, playwright-tests] | |
| if: | | |
| always() && | |
| needs.check-if-merged.outputs.should_run == 'true' && | |
| needs.build.result == 'success' && | |
| (needs.playwright-tests.result == 'success' || needs.playwright-tests.result == 'skipped') | |
| runs-on: blacksmith-2vcpu-ubuntu-2404 # trunk-ignore(actionlint/runner-label) | |
| steps: | |
| - name: Coveralls Finished | |
| uses: coverallsapp/github-action@v2 # zizmor: ignore[unpinned-uses] | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| parallel-finished: true | |
| fail-on-error: false | |
| # 5. Publishing job (main deployment logic) | |
| publish-github-packages: | |
| needs: [check-if-merged, build, playwright-tests, coverage-report] | |
| if: | | |
| always() && | |
| needs.check-if-merged.outputs.should_run == 'true' && | |
| needs.build.result == 'success' && | |
| (needs.playwright-tests.result == 'success' || needs.playwright-tests.result == 'skipped') && | |
| (needs.coverage-report.result == 'success' || needs.coverage-report.result == 'skipped') | |
| runs-on: ubuntu-latest | |
| environment: production | |
| permissions: | |
| contents: write | |
| packages: write | |
| issues: write | |
| pull-requests: write | |
| id-token: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 # zizmor: ignore[unpinned-uses] | |
| with: | |
| persist-credentials: false | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| fetch-depth: 0 | |
| - uses: pnpm/action-setup@v6 # zizmor: ignore[unpinned-uses] | |
| - name: Use Node.js - 24 | |
| uses: actions/setup-node@v7 # zizmor: ignore[unpinned-uses,cache-poisoning] | |
| with: | |
| node-version: 24 | |
| - name: Install | |
| run: pnpm install --filter . --prefer-offline --frozen-lockfile --ignore-scripts --config.engine-strict=false | |
| - name: Check Publishing Status | |
| id: publish-check | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| IS_MERGED: ${{ github.event.pull_request.merged }} | |
| HAS_SKIP_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'skip-publish') }} | |
| INPUT_VERSION: ${{ github.event.inputs.version_bump }} | |
| run: | | |
| # Validate event name first | |
| if [[ "$EVENT_NAME" != "pull_request" && "$EVENT_NAME" != "workflow_dispatch" ]]; then | |
| echo "Invalid event type" | |
| exit 1 | |
| fi | |
| # Check publishing conditions | |
| if [[ "$EVENT_NAME" == "pull_request" ]]; then | |
| if [[ "$IS_MERGED" != "true" ]]; then | |
| echo "PR not merged, skipping publish" | |
| echo "should_publish=false" >> $GITHUB_OUTPUT | |
| elif [[ "$HAS_SKIP_LABEL" == "true" ]]; then | |
| echo "Publishing skipped due to skip-publish label" | |
| echo "should_publish=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "should_publish=true" >> $GITHUB_OUTPUT | |
| fi | |
| elif [[ "$EVENT_NAME" == "workflow_dispatch" && "$INPUT_VERSION" == "skip" ]]; then | |
| echo "Publishing skipped via manual selection" | |
| echo "should_publish=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "should_publish=true" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Create Comment on Skip | |
| if: github.event_name == 'pull_request' && steps.publish-check.outputs.should_publish == 'false' | |
| uses: actions/github-script@v9 # zizmor: ignore[unpinned-uses] | |
| with: | |
| github-token: ${{ secrets.ACTIONS_KEY }} | |
| script: | | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: '⏭️ NPM publishing was skipped due to the `skip-publish` label.' | |
| }) | |
| - name: Import GPG key | |
| if: steps.publish-check.outputs.should_publish == 'true' | |
| id: import_gpg | |
| uses: crazy-max/ghaction-import-gpg@v7 # zizmor: ignore[unpinned-uses] | |
| with: | |
| gpg_private_key: ${{ secrets.ACTIONS_GPG_PRIVATE_KEY }} | |
| passphrase: ${{ secrets.ACTIONS_GPG_PASSPHRASE }} | |
| git_user_signingkey: true | |
| git_commit_gpgsign: true | |
| git_tag_gpgsign: true | |
| git_config_global: true | |
| - name: Determine version bump type | |
| if: steps.publish-check.outputs.should_publish == 'true' | |
| id: version-type | |
| env: | |
| HAS_MAJOR: ${{ contains(github.event.pull_request.labels.*.name, 'major') }} | |
| HAS_MINOR: ${{ contains(github.event.pull_request.labels.*.name, 'minor') }} | |
| INPUT_VERSION: ${{ github.event.inputs.version_bump }} | |
| run: | | |
| # Function to validate version bump type | |
| validate_bump_type() { | |
| local bump="$1" | |
| case "$bump" in | |
| "major"|"minor"|"patch"|"skip") echo "$bump" ;; | |
| *) echo "patch" ;; | |
| esac | |
| } | |
| # Determine and validate bump type | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| BUMP_TYPE=$(validate_bump_type "$INPUT_VERSION") | |
| if [ "$BUMP_TYPE" = "skip" ]; then | |
| echo "Publishing skipped via manual selection" | |
| echo "should_publish=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| elif [ "$HAS_MAJOR" = "true" ]; then | |
| BUMP_TYPE="major" | |
| elif [ "$HAS_MINOR" = "true" ]; then | |
| BUMP_TYPE="minor" | |
| else | |
| BUMP_TYPE="patch" | |
| fi | |
| echo "bump=$BUMP_TYPE" >> $GITHUB_OUTPUT | |
| - name: Bump version | |
| if: steps.publish-check.outputs.should_publish == 'true' | |
| id: version | |
| env: | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| PR_URL: ${{ github.event.pull_request.html_url }} | |
| BUMP_TYPE: ${{ steps.version-type.outputs.bump }} | |
| GITHUB_TOKEN: ${{ secrets.ACTIONS_KEY }} | |
| GPG_KEY_ID: ${{ steps.import_gpg.outputs.keyid }} | |
| run: | | |
| # Validate GPG key ID format first | |
| if [[ ! "$GPG_KEY_ID" =~ ^[A-F0-9]{16}$ ]]; then | |
| echo "Invalid GPG key ID format" | |
| exit 1 | |
| fi | |
| # Configure git with validated credentials | |
| git config --global user.name "GitHub Actions Bot" | |
| git config --global user.email "jason@humanspeak.com" | |
| git config --global commit.gpgsign true | |
| git config --global user.signingkey "$GPG_KEY_ID" | |
| # Set up authentication for push | |
| git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git" | |
| # Validate bump type for security | |
| case "$BUMP_TYPE" in | |
| "major"|"minor"|"patch") ;; | |
| *) | |
| echo "Invalid version bump type" | |
| exit 1 | |
| ;; | |
| esac | |
| # Mutate package.json only — do NOT parse pnpm stdout (pnpm 11 prints multi-line text) | |
| pnpm version "$BUMP_TYPE" --no-git-tag-version | |
| PACKAGE_VERSION=$(node -p "require('./package.json').version") | |
| NEW_VERSION="v${PACKAGE_VERSION}" | |
| echo "new_version=$NEW_VERSION" >> "$GITHUB_OUTPUT" | |
| # Keep tombstone shims in lockstep with the canonical version | |
| for SHIM in tombstones/svelte-diff tombstones/svelte-diff-match-patch; do | |
| node -e " | |
| const fs = require('fs') | |
| const path = process.argv[1] + '/package.json' | |
| const pkg = JSON.parse(fs.readFileSync(path, 'utf8')) | |
| pkg.version = process.argv[2] | |
| pkg.dependencies['@humanspeak/svelte-diff'] = '^' + process.argv[2] | |
| fs.writeFileSync(path, JSON.stringify(pkg, null, 4) + '\n') | |
| " "$SHIM" "$PACKAGE_VERSION" | |
| done | |
| # Escape special characters in PR title and URL | |
| ESCAPED_TITLE=$(echo "$PR_TITLE" | sed 's/[`$"\]/\\&/g') | |
| ESCAPED_URL=$(echo "$PR_URL" | sed 's/[`$"\]/\\&/g') | |
| # Refresh the managed README footer (the docs-kit ecosystem block + | |
| # standardized License/Credits). Runs AFTER `pnpm version` so the | |
| # working tree is clean when pnpm runs — pnpm version aborts on a | |
| # dirty tree. Fetched raw from the private docs-kit repo (no install, | |
| # no dependency here); reuses $GITHUB_TOKEN (ACTIONS_KEY) for auth. | |
| # Best-effort: a failed fetch or roster blip only warns. | |
| UPDATER="$RUNNER_TEMP/update-ecosystem-readme.mjs" | |
| if curl -fsSL -H "Authorization: token $GITHUB_TOKEN" \ | |
| https://raw.githubusercontent.com/humanspeak/docs-kit/main/scripts/update-ecosystem-readme.mjs \ | |
| -o "$UPDATER"; then | |
| node "$UPDATER" || echo "::warning::ecosystem updater errored; README left unchanged" | |
| else | |
| echo "::warning::could not fetch ecosystem updater; skipping README refresh" | |
| fi | |
| # Commit the version changes (and any refreshed README ecosystem block) | |
| git add package.json README.md tombstones/svelte-diff/package.json tombstones/svelte-diff-match-patch/package.json | |
| git commit -m "Bump version to ${NEW_VERSION} [skip ci]" | |
| # Create an annotated tag with release notes | |
| git tag -a "${NEW_VERSION}" -m "Release ${NEW_VERSION} | |
| Changes in this Release: | |
| - ${ESCAPED_TITLE} | |
| PR: ${ESCAPED_URL}" | |
| # Push changes | |
| git push | |
| git push --tags | |
| - name: Create Release | |
| if: steps.publish-check.outputs.should_publish == 'true' | |
| id: create_release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| NEW_VERSION: ${{ steps.version.outputs.new_version }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| CUSTOM_MESSAGE: ${{ github.event.inputs.custom_message }} | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| PR_URL: ${{ github.event.pull_request.html_url }} | |
| run: | | |
| BODY="Changes in this Release | |
| ${CUSTOM_MESSAGE:-$PR_TITLE}" | |
| if [[ "$EVENT_NAME" == "pull_request" ]]; then | |
| BODY="$BODY | |
| For more details, see the [Pull Request]($PR_URL)" | |
| fi | |
| gh release create "$NEW_VERSION" \ | |
| --title "Release $NEW_VERSION" \ | |
| --notes "$BODY" \ | |
| --latest | |
| - name: Publish | |
| if: steps.publish-check.outputs.should_publish == 'true' | |
| run: | | |
| # Clean up any existing npmrc files that might interfere with OIDC | |
| rm -f ./.npmrc ~/.npmrc | |
| # Configure registry for scoped package | |
| pnpm config set @humanspeak:registry https://registry.npmjs.org/ | |
| # Publish using pnpm with OIDC (trusted publishing) | |
| pnpm publish --provenance --access public --no-git-checks | |
| - name: Publish tombstone shims | |
| if: steps.publish-check.outputs.should_publish == 'true' | |
| run: | | |
| # Shims re-export the canonical package under the old/alias names. | |
| # Best-effort: a shim failure must not trigger "Cleanup on failure" | |
| # and roll back the main release that already shipped. | |
| # The old-name shim carries a "deprecated" field in its manifest, so | |
| # each published version arrives pre-deprecated — no npm deprecate | |
| # call (or extra token) needed here. | |
| pnpm --dir tombstones/svelte-diff-match-patch publish --provenance --access public --no-git-checks || | |
| echo "::warning::@humanspeak/svelte-diff-match-patch shim publish failed — publish manually from tombstones/" | |
| pnpm --dir tombstones/svelte-diff publish --provenance --access public --no-git-checks || | |
| echo "::warning::svelte-diff alias publish failed — publish manually from tombstones/" | |
| - name: Cleanup on failure | |
| if: failure() | |
| env: | |
| RELEASE_VERSION: ${{ steps.version.outputs.new_version }} | |
| GITHUB_TOKEN: ${{ secrets.ACTIONS_KEY }} | |
| run: | | |
| # Validate version format first | |
| if [[ ! "$RELEASE_VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "Invalid version format: $RELEASE_VERSION" | |
| exit 1 | |
| fi | |
| # Proceed with cleanup only if validation passes | |
| gh release delete "$RELEASE_VERSION" --yes || true | |
| git tag -d "$RELEASE_VERSION" || true | |
| git push --delete origin "$RELEASE_VERSION" || true | |
| - name: Notify on failure | |
| if: failure() | |
| uses: actions/github-script@v9 # zizmor: ignore[unpinned-uses] | |
| with: | |
| github-token: ${{ secrets.ACTIONS_KEY }} | |
| script: | | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: '❌ Release workflow failed. Please check the [workflow logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})' | |
| }) |