< Summary - go-semantic-release Coverage

Line coverage
72%
Covered lines: 32
Uncovered lines: 12
Coverable lines: 44
Total lines: 110
Line coverage: 72.7%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Publish0%0072.73%

File(s)

/home/runner/work/go-semantic-release/go-semantic-release/internal/adapters/github/publisher.go

#LineLine coverage
 1package github
 2
 3import (
 4  "bytes"
 5  "context"
 6  "encoding/json"
 7  "fmt"
 8  "io"
 9  "net/http"
 10  "os"
 11
 12  "github.com/jedi-knights/go-semantic-release/internal/domain"
 13  "github.com/jedi-knights/go-semantic-release/internal/ports"
 14)
 15
 16// Compile-time interface compliance check.
 17var _ ports.ReleasePublisher = (*Publisher)(nil)
 18
 19// Publisher implements ports.ReleasePublisher for GitHub Releases.
 20type Publisher struct {
 21  owner  string
 22  repo   string
 23  token  string
 24  client *http.Client
 25}
 26
 27// NewPublisher creates a GitHub release publisher.
 28// If token is empty, it is resolved from GH_TOKEN, GITHUB_TOKEN, or
 29// SEMANTIC_RELEASE_GITHUB_TOKEN environment variables.
 30func NewPublisher(owner, repo, token string) *Publisher {
 31  if token == "" {
 32    for _, key := range []string{"GH_TOKEN", "GITHUB_TOKEN", "SEMANTIC_RELEASE_GITHUB_TOKEN"} {
 33      if v := os.Getenv(key); v != "" {
 34        token = v
 35        break
 36      }
 37    }
 38  }
 39  return &Publisher{
 40    owner:  owner,
 41    repo:   repo,
 42    token:  token,
 43    client: &http.Client{},
 44  }
 45}
 46
 47type createReleaseRequest struct {
 48  TagName    string `json:"tag_name"`
 49  Name       string `json:"name"`
 50  Body       string `json:"body"`
 51  Prerelease bool   `json:"prerelease"`
 52  Draft      bool   `json:"draft"`
 53}
 54
 55type createReleaseResponse struct {
 56  HTMLURL string `json:"html_url"`
 57  ID      int    `json:"id"`
 58}
 59
 360func (p *Publisher) Publish(ctx context.Context, params ports.PublishParams) (domain.ProjectReleaseResult, error) {
 361  name := params.TagName
 162  if params.Project != "" {
 163    name = fmt.Sprintf("%s %s", params.Project, params.Version.String())
 164  }
 65
 366  reqBody := createReleaseRequest{
 367    TagName:    params.TagName,
 368    Name:       name,
 369    Body:       params.Changelog,
 370    Prerelease: params.Prerelease,
 371  }
 372
 373  jsonData, err := json.Marshal(reqBody)
 074  if err != nil {
 075    return domain.ProjectReleaseResult{}, fmt.Errorf("marshaling release request: %w", err)
 076  }
 77
 378  url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases", p.owner, p.repo)
 379  req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(jsonData))
 080  if err != nil {
 081    return domain.ProjectReleaseResult{}, fmt.Errorf("creating request: %w", err)
 082  }
 83
 384  req.Header.Set("Authorization", "token "+p.token)
 385  req.Header.Set("Content-Type", "application/json")
 386  req.Header.Set("Accept", "application/vnd.github+json")
 387
 388  resp, err := p.client.Do(req)
 089  if err != nil {
 090    return domain.ProjectReleaseResult{}, fmt.Errorf("publishing release: %w", err)
 091  }
 392  defer func() { _ = resp.Body.Close() }()
 93
 194  if resp.StatusCode != http.StatusCreated {
 195    body, _ := io.ReadAll(resp.Body)
 196    return domain.ProjectReleaseResult{}, fmt.Errorf("github release failed (%d): %s", resp.StatusCode, string(body))
 197  }
 98
 299  var releaseResp createReleaseResponse
 0100  if err := json.NewDecoder(resp.Body).Decode(&releaseResp); err != nil {
 0101    return domain.ProjectReleaseResult{}, fmt.Errorf("decoding release response: %w", err)
 0102  }
 103
 2104  return domain.ProjectReleaseResult{
 2105    TagName:    params.TagName,
 2106    Published:  true,
 2107    PublishURL: releaseResp.HTMLURL,
 2108    Changelog:  params.Changelog,
 2109  }, nil
 110}

Methods/Properties

Publish