• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ggilder / codecoverage / 14650332767

24 Apr 2025 07:45PM UTC coverage: 70.68% (-11.1%) from 81.763%
14650332767

push

github

web-flow
Update dependencies (#24)

72 of 110 branches covered (65.45%)

Branch coverage included in aggregate %.

5 of 13 new or added lines in 4 files covered. (38.46%)

13 existing lines in 3 files now uncovered.

292 of 405 relevant lines covered (72.1%)

3.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.42
/src/utils/github.ts
1
import * as core from '@actions/core'
2✔
2
import * as diff from './diff'
2✔
3
import * as github from '@actions/github'
1✔
4
import {
2✔
5
  CoverageFile,
1✔
6
  LineRange,
7
  coalesceLineNumbers,
1✔
8
  intersectLineRanges
9
} from './general'
10
import {Octokit} from 'octokit'
1✔
11

1!
12
export class GithubUtil {
1✔
13
  private client: Octokit
14

1✔
15
  constructor(token: string, baseUrl: string) {
1✔
16
    if (!token) {
3✔
17
      throw new Error('GITHUB_TOKEN is missing')
1✔
18
    }
1✔
19
    this.client = new Octokit({auth: token, baseUrl})
2!
20
  }
3✔
21

22
  getPullRequestRef(): string {
1✔
23
    const pullRequest = github.context.payload.pull_request
24
    return pullRequest
25
      ? pullRequest.head.ref
26
      : github.context.ref.replace('refs/heads/', '')
27
  }
28

29
  async getPullRequestDiff(): Promise<PullRequestFiles> {
1✔
30
    const pull_number = github.context.issue.number
31
    const response = await this.client.rest.pulls.get({
32
      ...github.context.repo,
UNCOV
33
      pull_number,
×
UNCOV
34
      mediaType: {
×
UNCOV
35
        format: 'diff'
×
UNCOV
36
      }
×
UNCOV
37
    })
×
38
    const fileLines = diff.parseGitDiff(response.data as unknown as string)
39
    const prFiles: PullRequestFiles = {}
×
UNCOV
40
    for (const item of fileLines) {
×
41
      prFiles[item.filename] = coalesceLineNumbers(item.addedLines)
42
    }
43

UNCOV
44
    return prFiles
×
45
  }
46

47
  /**
48
   * https://docs.github.com/en/rest/checks/runs?apiVersion=2022-11-28#create-a-check-run
49
   * https://docs.github.com/en/rest/checks/runs?apiVersion=2022-11-28#update-a-check-run
50
   */
51
  async annotate(input: InputAnnotateParams): Promise<number> {
1✔
52
    if (input.annotations.length === 0) {
53
      return 0
54
    }
55
    // github API lets you post 50 annotations at a time
56
    const chunkSize = 50
×
UNCOV
57
    const chunks: Annotations[][] = []
×
58
    for (let i = 0; i < input.annotations.length; i += chunkSize) {
59
      chunks.push(input.annotations.slice(i, i + chunkSize))
60
    }
61
    let lastResponseStatus = 0
62
    let checkId
63
    for (let i = 0; i < chunks.length; i++) {
NEW
64
      let status: 'in_progress' | 'completed' | 'queued' = 'in_progress'
×
NEW
65
      let conclusion:
×
66
        | 'success'
67
        | 'action_required'
×
68
        | 'cancelled'
×
69
        | 'failure'
70
        | 'neutral'
71
        | 'skipped'
72
        | 'stale'
73
        | 'timed_out'
74
        | undefined = undefined
75
      if (i === chunks.length - 1) {
76
        status = 'completed'
77
        conclusion = 'success'
78
      }
79
      const params = {
80
        ...github.context.repo,
81
        name: 'Annotate',
UNCOV
82
        head_sha: input.referenceCommitHash,
×
UNCOV
83
        status,
×
84
        ...(conclusion && {conclusion}),
85
        output: {
86
          title: 'Coverage Tool',
87
          summary: 'Missing Coverage',
88
          annotations: chunks[i]
89
        }
90
      }
91
      let response
92
      if (i === 0) {
93
        response = await this.client.rest.checks.create({
94
          ...params
95
        })
96
        checkId = response.data.id
97
      } else {
98
        response = await this.client.rest.checks.update({
99
          ...params,
100
          check_run_id: checkId,
101
          status: 'in_progress' as const
102
        })
103
      }
104
      core.info(response.data.output.annotations_url)
105
      lastResponseStatus = response.status
106
    }
107
    return lastResponseStatus
108
  }
109

110
  buildAnnotations(
1✔
111
    coverageFiles: CoverageFile[],
1✔
112
    pullRequestFiles: PullRequestFiles
1✔
113
  ): Annotations[] {
1✔
114
    const annotations: Annotations[] = []
1✔
115
    for (const current of coverageFiles) {
1✔
116
      // Only annotate relevant files
117
      const prFileRanges = pullRequestFiles[current.fileName]
3✔
118
      if (prFileRanges) {
3✔
119
        const coverageRanges = coalesceLineNumbers(current.missingLineNumbers)
2✔
120
        const uncoveredRanges = intersectLineRanges(
2✔
121
          coverageRanges,
2✔
122
          prFileRanges
2✔
123
        )
2✔
124

125
        // Only annotate relevant line ranges
126
        for (const uRange of uncoveredRanges) {
2✔
127
          const message =
4✔
128
            uRange.end_line > uRange.start_line
4✔
129
              ? 'These lines are not covered by a test'
1✔
130
              : 'This line is not covered by a test'
3✔
131
          annotations.push({
4✔
132
            path: current.fileName,
4✔
133
            start_line: uRange.start_line,
4✔
134
            end_line: uRange.end_line,
4✔
135
            annotation_level: 'warning',
4✔
136
            message
4✔
137
          })
4✔
138
        }
4✔
139
      }
2✔
140
    }
3✔
141
    core.info(`Annotation count: ${annotations.length}`)
1✔
142
    return annotations
1✔
143
  }
1✔
144
}
1✔
145

146
type InputAnnotateParams = {
147
  referenceCommitHash: string
148
  annotations: Annotations[]
149
}
150

151
type Annotations = {
152
  path: string
153
  start_line: number
154
  end_line: number
155
  start_column?: number
156
  end_column?: number
157
  annotation_level: 'notice' | 'warning' | 'failure'
158
  message: string
159
}
160

161
type PullRequestFiles = {
162
  [key: string]: LineRange[]
163
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc