2024-08-16 00:41:38 -06:00
|
|
|
package objdiff
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2024-08-29 22:37:30 -06:00
|
|
|
"github.com/encounter/decompal/common"
|
|
|
|
|
"github.com/encounter/decompal/database"
|
2024-08-16 00:41:38 -06:00
|
|
|
"github.com/encounter/decompal/zipstream"
|
|
|
|
|
"github.com/google/go-github/v63/github"
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
|
"github.com/rs/zerolog"
|
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
|
|
|
"io"
|
|
|
|
|
"net/http"
|
|
|
|
|
"regexp"
|
|
|
|
|
"sort"
|
|
|
|
|
"strings"
|
2024-08-29 22:37:30 -06:00
|
|
|
"time"
|
2024-08-16 00:41:38 -06:00
|
|
|
)
|
|
|
|
|
|
2024-08-23 00:30:18 -06:00
|
|
|
var artifactNameRegex = regexp.MustCompile(`^(?P<version>[A-z0-9_\-]+)[_-]report(?:[_-].*)?$`)
|
2024-08-16 00:41:38 -06:00
|
|
|
|
|
|
|
|
func FetchReportFiles(
|
|
|
|
|
ctx context.Context,
|
2024-08-29 22:37:30 -06:00
|
|
|
db *database.DB,
|
2024-08-16 00:41:38 -06:00
|
|
|
logger zerolog.Logger,
|
|
|
|
|
client *github.Client,
|
2024-08-29 22:37:30 -06:00
|
|
|
project *common.Project,
|
|
|
|
|
commit *common.Commit,
|
2024-08-16 00:41:38 -06:00
|
|
|
runId int64,
|
2024-08-29 22:37:30 -06:00
|
|
|
) ([]common.ReportFile, error) {
|
2024-08-16 00:41:38 -06:00
|
|
|
logger = logger.With().
|
2024-08-29 22:37:30 -06:00
|
|
|
Str("commit_sha", commit.Sha).
|
2024-08-16 00:41:38 -06:00
|
|
|
Int64("workflow_run_id", runId).
|
|
|
|
|
Logger()
|
|
|
|
|
|
2024-08-29 22:37:30 -06:00
|
|
|
artifacts, _, err := client.Actions.ListWorkflowRunArtifacts(ctx, project.Owner, project.Name, runId, nil)
|
2024-08-16 00:41:38 -06:00
|
|
|
if err != nil {
|
|
|
|
|
logger.Error().
|
|
|
|
|
Err(err).
|
|
|
|
|
Msg("Failed to list workflow run artifacts")
|
|
|
|
|
return nil, errors.Wrap(err, "failed to list workflow run artifacts")
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-29 22:37:30 -06:00
|
|
|
files := make([]common.ReportFile, 0)
|
2024-08-16 00:41:38 -06:00
|
|
|
for _, artifact := range artifacts.Artifacts {
|
|
|
|
|
logger := logger.With().
|
|
|
|
|
Str("artifact_name", artifact.GetName()).
|
|
|
|
|
Int64("artifact_id", artifact.GetID()).
|
|
|
|
|
Logger()
|
|
|
|
|
|
|
|
|
|
matches := artifactNameRegex.FindStringSubmatch(artifact.GetName())
|
|
|
|
|
if matches == nil {
|
|
|
|
|
//logger.Debug().Msg("Skipping artifact")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
version := matches[artifactNameRegex.SubexpIndex("version")]
|
|
|
|
|
|
2024-08-29 22:37:30 -06:00
|
|
|
start := time.Now()
|
|
|
|
|
existing, err := db.GetReport(ctx, project.ID, version, commit.Sha)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Fatal().Err(err).Msg("failed to check if report exists")
|
|
|
|
|
}
|
|
|
|
|
if existing != nil {
|
|
|
|
|
end := time.Now()
|
|
|
|
|
logger.Info().
|
|
|
|
|
Str("duration", end.Sub(start).String()).
|
|
|
|
|
Msg("Report already exists")
|
|
|
|
|
files = append(files, *existing)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
artifactUrl, _, err := client.Actions.DownloadArtifact(ctx, project.Owner, project.Name, artifact.GetID(), 3)
|
2024-08-16 00:41:38 -06:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "failed to get artifact download url")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, artifactUrl.String(), nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "failed to create download request")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.Header.Set("User-Agent", client.UserAgent)
|
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "failed to download artifact")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
report, err := findReportFile(logger, resp.Body)
|
|
|
|
|
_ = resp.Body.Close()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if report != nil {
|
2024-08-29 22:37:30 -06:00
|
|
|
file := common.ReportFile{
|
|
|
|
|
Project: project,
|
2024-08-16 00:41:38 -06:00
|
|
|
Version: version,
|
2024-08-29 22:37:30 -06:00
|
|
|
Commit: commit,
|
2024-08-16 00:41:38 -06:00
|
|
|
Report: report,
|
2024-08-29 22:37:30 -06:00
|
|
|
}
|
|
|
|
|
start := time.Now()
|
|
|
|
|
if err = db.InsertReport(ctx, &file); err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "failed to insert report")
|
|
|
|
|
}
|
|
|
|
|
end := time.Now()
|
|
|
|
|
logger.Info().
|
|
|
|
|
Str("duration", end.Sub(start).String()).
|
|
|
|
|
Msg("Inserted report")
|
|
|
|
|
files = append(files, file)
|
2024-08-16 00:41:38 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sort files by version
|
|
|
|
|
sort.Slice(files, func(i, j int) bool {
|
|
|
|
|
return files[i].Version < files[j].Version
|
|
|
|
|
})
|
|
|
|
|
return files, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// findReportFile reads the zip stream and writes the report file to the output path
|
|
|
|
|
// Returns true if the report file was found and written
|
2024-08-29 22:37:30 -06:00
|
|
|
func findReportFile(logger zerolog.Logger, r io.Reader) (*common.Report, error) {
|
2024-08-16 00:41:38 -06:00
|
|
|
zr := zipstream.NewReader(r)
|
|
|
|
|
for {
|
|
|
|
|
entry, err := zr.Next()
|
|
|
|
|
if err != nil {
|
|
|
|
|
if err == io.EOF {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
return nil, errors.Wrap(err, "failed to get next entry")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data, err := io.ReadAll(entry)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "failed to read report file")
|
|
|
|
|
}
|
|
|
|
|
if strings.HasSuffix(entry.Name, "report.json") {
|
2024-08-29 22:37:30 -06:00
|
|
|
report := &common.Report{}
|
|
|
|
|
err := common.ParseReportJson(data, report)
|
2024-08-16 00:41:38 -06:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "failed to read report file")
|
|
|
|
|
}
|
|
|
|
|
logger.Info().
|
|
|
|
|
Str("filename", entry.Name).
|
|
|
|
|
Msg("Read report file")
|
|
|
|
|
return report, nil
|
|
|
|
|
} else if strings.HasSuffix(entry.Name, "report.binpb") ||
|
|
|
|
|
strings.HasSuffix(entry.Name, "report.pb") {
|
2024-08-29 22:37:30 -06:00
|
|
|
report := &common.Report{}
|
2024-08-16 00:41:38 -06:00
|
|
|
err = proto.Unmarshal(data, report)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
logger.Info().
|
|
|
|
|
Str("filename", entry.Name).
|
|
|
|
|
Msg("Read report file")
|
|
|
|
|
return report, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|