Skip to content
Latchkey

ecosystem-ci trigger workflow (vitejs/vite)

The ecosystem-ci trigger workflow from vitejs/vite, explained and optimized by Latchkey.

A

CI health: A - excellent

Run this on Latchkey for self-healing, caching, and up to 58% lower cost.

Grade your own workflow free or run it on Latchkey →
Source: vitejs/vite.github/workflows/ecosystem-ci-trigger.ymlLicense MITView source

What it does

This is the ecosystem-ci trigger workflow from the vitejs/vite repository, a real project running GitHub Actions. It is shown here with attribution under its MIT license.

Below, Latchkey shows a faster, safer version produced by its optimization engine.

The workflow

workflow (.yml)
name: ecosystem-ci trigger

on:
  issue_comment:
    types: [created]

jobs:
  trigger:
    runs-on: ubuntu-slim
    if: github.repository == 'vitejs/vite' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/ecosystem-ci run')
    permissions:
      issues: write # to add / delete reactions, post comments
      pull-requests: write # to read PR data, and to add labels
      actions: read # to check workflow status
    steps:
      - name: Check User Permissions
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
        id: check-permissions
        with:
          script: |
            const user = context.payload.sender.login
            console.log(`Validate user: ${user}`)

            const additionalAllowedUsers = ['lukastaegert']

            let hasTriagePermission = false
            try {
              const { data } = await github.rest.repos.getCollaboratorPermissionLevel({
                owner: context.repo.owner,
                repo: context.repo.repo,
                username: user,
              });
              hasTriagePermission = data.user.permissions.triage
            } catch (e) {
              console.warn(e)
            }

            if (hasTriagePermission || additionalAllowedUsers.includes(user)) {
              console.log('User is allowed. Adding +1 reaction.')
              await github.rest.reactions.createForIssueComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: context.payload.comment.id,
                content: '+1',
              })
            } else {
              console.log('User is not allowed. Adding -1 reaction.')
              await github.rest.reactions.createForIssueComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: context.payload.comment.id,
                content: '-1',
              })
              throw new Error('User does not have the necessary permissions.')
            }

      - name: Get PR Data
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
        id: get-pr-data
        with:
          script: |
            console.log(`Get PR info: ${context.repo.owner}/${context.repo.repo}#${context.issue.number}`)
            const { data: pr } = await github.rest.pulls.get({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.issue.number
            })

            const commentCreatedAt = new Date(context.payload.comment.created_at)

            const { data: headCommit } = await github.rest.repos.getCommit({
              owner: context.repo.owner,
              repo: context.repo.repo,
              ref: pr.head.sha,
            })
            const commitPushedAt = new Date(headCommit.commit.committer.date)

            console.log(`Comment created at: ${commentCreatedAt.toISOString()}`)
            console.log(`Head commit (${pr.head.sha}) date: ${commitPushedAt.toISOString()}`)

            // Check if any commits were pushed after the comment was created
            if (commitPushedAt > commentCreatedAt) {
              const errorMsg = [
                '⚠️ Security warning: PR was updated after the trigger command was posted.',
                '',
                `Comment posted at: ${commentCreatedAt.toISOString()}`,
                `Head commit (${pr.head.sha}) date: ${commitPushedAt.toISOString()}`,
                '',
                'This could indicate an attempt to inject code after approval.',
                'Please review the latest changes and re-run /ecosystem-ci run if they are acceptable.'
              ].join('\n')

              core.setFailed(errorMsg)

              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                body: errorMsg
              })

              throw new Error('PR was pushed to after comment was created')
            }

            core.setOutput('head_sha', pr.head.sha)
            return {
              num: context.issue.number,
              branchName: pr.head.ref,
              commit: pr.head.sha,
              repo: pr.head.repo.full_name
            }

      - name: Check Package Existence
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
        id: check-package
        env:
          PR_DATA: ${{ steps.get-pr-data.outputs.result }}
        with:
          script: |
            const prData = JSON.parse(process.env.PR_DATA)
            const url = `https://pkg.pr.new/vite@${prData.commit}`
            const response = await fetch(url)
            console.log(`Package check URL: ${url}, Status: ${response.status}`)

            // Add 'rocket' reaction to the issue comment
            if (response.status === 404) {
              const { data: reaction } = await github.rest.reactions.createForIssueComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: context.payload.comment.id,
                content: 'rocket',
              })
              return { exists: false, reaction: reaction.id }
            }

            return { exists: true, reaction: null }

      - name: Generate Token
        id: generate-token
        uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
        with:
          client-id: ${{ vars.ECOSYSTEM_CI_GITHUB_APP_CLIENT_ID }}
          private-key: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_PRIVATE_KEY }}
          repositories: |
            vite
            vite-ecosystem-ci
          permission-pull-requests: write # to add pr labels, add / delete comment reactions
          permission-actions: write # to list workflow runs, to dispatch workflows

      - name: Trigger Preview Release (if Package Not Found)
        if: fromJSON(steps.check-package.outputs.result).exists == false
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
        id: trigger-preview-release
        env:
          PR_DATA: ${{ steps.get-pr-data.outputs.result }}
        with:
          github-token: ${{ steps.generate-token.outputs.token }}
          script: |
            const prData = JSON.parse(process.env.PR_DATA)
            console.log('Package not found, triggering preview release...')

            // Add label "trigger: preview" to the PR
            await github.rest.issues.addLabels({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: prData.num,
              labels: ['trigger: preview']
            })
            console.log('Added "trigger: preview" label.')

      - name: Wait for Preview Release Completion (if Package Not Found)
        if: fromJSON(steps.check-package.outputs.result).exists == false
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
        id: wait-preview-release
        env:
          PR_DATA: ${{ steps.get-pr-data.outputs.result }}
          REACTION: ${{ fromJSON(steps.check-package.outputs.result).reaction }}
        with:
          script: |
            const prData = JSON.parse(process.env.PR_DATA)
            const reaction = +process.env.REACTION
            const workflowFileName = 'preview-release.yml'
            const workflow = await github.rest.actions.getWorkflow({
              owner: context.repo.owner,
              repo: context.repo.repo,
              workflow_id: workflowFileName,
            })
            const workflowId = workflow.data.id
            console.log(`Waiting for workflow ID ${workflowId} to complete...`)

            const maxRetries = 60 // Wait up to 10 minutes
            const delay = 10000 // 10 seconds
            let completed = false

            for (let i = 0; i < maxRetries; i++) {
              const runsData = await github.rest.actions.listWorkflowRuns({
                owner: context.repo.owner,
                repo: context.repo.repo,
                workflow_id: workflowId,
                head_sha: prData.commit,
                per_page: 100,
                page: 1,
              })

              const runs = runsData.data.workflow_runs

              if (runs.length > 0) {
                const latestRun = runs[0]
                console.log(`Latest run status: ${latestRun.status}, conclusion: ${latestRun.conclusion}`)
                if (latestRun.status === 'completed') {
                  if (latestRun.conclusion === 'success') {
                    console.log('Preview release workflow completed successfully.')
                    completed = true
                    break
                  } else if (latestRun.conclusion === 'skipped') {
                   // noop
                  } else {
                    throw new Error('Preview Release workflow failed.')
                  }
                }
              }

              console.log(`Retrying... (${i + 1}/${maxRetries})`)
              await new Promise(resolve => setTimeout(resolve, delay))
            }

            if (!completed) {
              throw new Error('Preview Release workflow did not complete in time.')
            }

            // Remove the 'rocket' reaction
            if (reaction) {
              await github.rest.reactions.deleteForIssueComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: context.payload.comment.id,
                reaction_id: reaction,
              })
              console.log('Removed "rocket" reaction.')
            }

      - name: Trigger Downstream Workflow
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
        id: trigger
        env:
          COMMENT: ${{ github.event.comment.body }}
          PR_DATA: ${{ steps.get-pr-data.outputs.result }}
        with:
          github-token: ${{ steps.generate-token.outputs.token }}
          script: |
            const comment = process.env.COMMENT.trim()
            const prData = JSON.parse(process.env.PR_DATA)

            const suite = comment.split('\n')[0].replace(/^\/ecosystem-ci run/, '').trim()

            await github.rest.actions.createWorkflowDispatch({
              owner: context.repo.owner,
              repo: 'vite-ecosystem-ci',
              workflow_id: 'ecosystem-ci-from-pr.yml',
              ref: 'main',
              inputs: {
                prNumber: '' + prData.num,
                branchName: prData.branchName,
                repo: prData.repo,
                commit: prData.commit,
                suite: suite === '' ? '-' : suite
              }
            })

The same workflow, on Latchkey

Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.

name: ecosystem-ci trigger
 
on:
  issue_comment:
    types: [created]
 
jobs:
  trigger:
    timeout-minutes: 30
    runs-on: ubuntu-slim
    if: github.repository == 'vitejs/vite' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/ecosystem-ci run')
    permissions:
      issues: write # to add / delete reactions, post comments
      pull-requests: write # to read PR data, and to add labels
      actions: read # to check workflow status
    steps:
      - name: Check User Permissions
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
        id: check-permissions
        with:
          script: |
            const user = context.payload.sender.login
            console.log(`Validate user: ${user}`)
 
            const additionalAllowedUsers = ['lukastaegert']
 
            let hasTriagePermission = false
            try {
              const { data } = await github.rest.repos.getCollaboratorPermissionLevel({
                owner: context.repo.owner,
                repo: context.repo.repo,
                username: user,
              });
              hasTriagePermission = data.user.permissions.triage
            } catch (e) {
              console.warn(e)
            }
 
            if (hasTriagePermission || additionalAllowedUsers.includes(user)) {
              console.log('User is allowed. Adding +1 reaction.')
              await github.rest.reactions.createForIssueComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: context.payload.comment.id,
                content: '+1',
              })
            } else {
              console.log('User is not allowed. Adding -1 reaction.')
              await github.rest.reactions.createForIssueComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: context.payload.comment.id,
                content: '-1',
              })
              throw new Error('User does not have the necessary permissions.')
            }
 
      - name: Get PR Data
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
        id: get-pr-data
        with:
          script: |
            console.log(`Get PR info: ${context.repo.owner}/${context.repo.repo}#${context.issue.number}`)
            const { data: pr } = await github.rest.pulls.get({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.issue.number
            })
 
            const commentCreatedAt = new Date(context.payload.comment.created_at)
 
            const { data: headCommit } = await github.rest.repos.getCommit({
              owner: context.repo.owner,
              repo: context.repo.repo,
              ref: pr.head.sha,
            })
            const commitPushedAt = new Date(headCommit.commit.committer.date)
 
            console.log(`Comment created at: ${commentCreatedAt.toISOString()}`)
            console.log(`Head commit (${pr.head.sha}) date: ${commitPushedAt.toISOString()}`)
 
            // Check if any commits were pushed after the comment was created
            if (commitPushedAt > commentCreatedAt) {
              const errorMsg = [
                '⚠️ Security warning: PR was updated after the trigger command was posted.',
                '',
                `Comment posted at: ${commentCreatedAt.toISOString()}`,
                `Head commit (${pr.head.sha}) date: ${commitPushedAt.toISOString()}`,
                '',
                'This could indicate an attempt to inject code after approval.',
                'Please review the latest changes and re-run /ecosystem-ci run if they are acceptable.'
              ].join('\n')
 
              core.setFailed(errorMsg)
 
              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                body: errorMsg
              })
 
              throw new Error('PR was pushed to after comment was created')
            }
 
            core.setOutput('head_sha', pr.head.sha)
            return {
              num: context.issue.number,
              branchName: pr.head.ref,
              commit: pr.head.sha,
              repo: pr.head.repo.full_name
            }
 
      - name: Check Package Existence
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
        id: check-package
        env:
          PR_DATA: ${{ steps.get-pr-data.outputs.result }}
        with:
          script: |
            const prData = JSON.parse(process.env.PR_DATA)
            const url = `https://pkg.pr.new/vite@${prData.commit}`
            const response = await fetch(url)
            console.log(`Package check URL: ${url}, Status: ${response.status}`)
 
            // Add 'rocket' reaction to the issue comment
            if (response.status === 404) {
              const { data: reaction } = await github.rest.reactions.createForIssueComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: context.payload.comment.id,
                content: 'rocket',
              })
              return { exists: false, reaction: reaction.id }
            }
 
            return { exists: true, reaction: null }
 
      - name: Generate Token
        id: generate-token
        uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
        with:
          client-id: ${{ vars.ECOSYSTEM_CI_GITHUB_APP_CLIENT_ID }}
          private-key: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_PRIVATE_KEY }}
          repositories: |
            vite
            vite-ecosystem-ci
          permission-pull-requests: write # to add pr labels, add / delete comment reactions
          permission-actions: write # to list workflow runs, to dispatch workflows
 
      - name: Trigger Preview Release (if Package Not Found)
        if: fromJSON(steps.check-package.outputs.result).exists == false
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
        id: trigger-preview-release
        env:
          PR_DATA: ${{ steps.get-pr-data.outputs.result }}
        with:
          github-token: ${{ steps.generate-token.outputs.token }}
          script: |
            const prData = JSON.parse(process.env.PR_DATA)
            console.log('Package not found, triggering preview release...')
 
            // Add label "trigger: preview" to the PR
            await github.rest.issues.addLabels({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: prData.num,
              labels: ['trigger: preview']
            })
            console.log('Added "trigger: preview" label.')
 
      - name: Wait for Preview Release Completion (if Package Not Found)
        if: fromJSON(steps.check-package.outputs.result).exists == false
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
        id: wait-preview-release
        env:
          PR_DATA: ${{ steps.get-pr-data.outputs.result }}
          REACTION: ${{ fromJSON(steps.check-package.outputs.result).reaction }}
        with:
          script: |
            const prData = JSON.parse(process.env.PR_DATA)
            const reaction = +process.env.REACTION
            const workflowFileName = 'preview-release.yml'
            const workflow = await github.rest.actions.getWorkflow({
              owner: context.repo.owner,
              repo: context.repo.repo,
              workflow_id: workflowFileName,
            })
            const workflowId = workflow.data.id
            console.log(`Waiting for workflow ID ${workflowId} to complete...`)
 
            const maxRetries = 60 // Wait up to 10 minutes
            const delay = 10000 // 10 seconds
            let completed = false
 
            for (let i = 0; i < maxRetries; i++) {
              const runsData = await github.rest.actions.listWorkflowRuns({
                owner: context.repo.owner,
                repo: context.repo.repo,
                workflow_id: workflowId,
                head_sha: prData.commit,
                per_page: 100,
                page: 1,
              })
 
              const runs = runsData.data.workflow_runs
 
              if (runs.length > 0) {
                const latestRun = runs[0]
                console.log(`Latest run status: ${latestRun.status}, conclusion: ${latestRun.conclusion}`)
                if (latestRun.status === 'completed') {
                  if (latestRun.conclusion === 'success') {
                    console.log('Preview release workflow completed successfully.')
                    completed = true
                    break
                  } else if (latestRun.conclusion === 'skipped') {
                   // noop
                  } else {
                    throw new Error('Preview Release workflow failed.')
                  }
                }
              }
 
              console.log(`Retrying... (${i + 1}/${maxRetries})`)
              await new Promise(resolve => setTimeout(resolve, delay))
            }
 
            if (!completed) {
              throw new Error('Preview Release workflow did not complete in time.')
            }
 
            // Remove the 'rocket' reaction
            if (reaction) {
              await github.rest.reactions.deleteForIssueComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: context.payload.comment.id,
                reaction_id: reaction,
              })
              console.log('Removed "rocket" reaction.')
            }
 
      - name: Trigger Downstream Workflow
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
        id: trigger
        env:
          COMMENT: ${{ github.event.comment.body }}
          PR_DATA: ${{ steps.get-pr-data.outputs.result }}
        with:
          github-token: ${{ steps.generate-token.outputs.token }}
          script: |
            const comment = process.env.COMMENT.trim()
            const prData = JSON.parse(process.env.PR_DATA)
 
            const suite = comment.split('\n')[0].replace(/^\/ecosystem-ci run/, '').trim()
 
            await github.rest.actions.createWorkflowDispatch({
              owner: context.repo.owner,
              repo: 'vite-ecosystem-ci',
              workflow_id: 'ecosystem-ci-from-pr.yml',
              ref: 'main',
              inputs: {
                prNumber: '' + prData.num,
                branchName: prData.branchName,
                repo: prData.repo,
                commit: prData.commit,
                suite: suite === '' ? '-' : suite
              }
            })
 

What changed

This workflow runs 1 job per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow