about summary refs log tree commit diff stats
path: root/internal/importer/main.go
blob: 5343e81b6d8b4b83460fbd8b3a75f99a5f021a9b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package importer

import (
	"context"
	"io"
	"log/slog"
	"os/exec"
	"path"
	"searchix/internal/config"
	"searchix/internal/fetcher"
	"searchix/internal/index"
	"slices"
	"strings"

	"github.com/pkg/errors"
)

func Start(
	cfg *config.Config,
	indexer *index.WriteIndex,
	forceUpdate bool,
	onlyUpdateSources *[]string,
) error {
	if len(cfg.Importer.Sources) == 0 {
		slog.Info("No sources enabled")

		return nil
	}

	ctx, cancel := context.WithTimeout(context.Background(), cfg.Importer.Timeout.Duration)
	defer cancel()

	forceUpdate = forceUpdate || (onlyUpdateSources != nil && len(*onlyUpdateSources) > 0)

	for name, source := range cfg.Importer.Sources {
		if onlyUpdateSources != nil && len(*onlyUpdateSources) > 0 {
			if !slices.Contains(*onlyUpdateSources, name) {
				continue
			}
		}

		logger := slog.With("name", name, "fetcher", source.Fetcher.String())
		logger.Debug("starting fetcher")

		fetcherDataPath := path.Join(cfg.DataPath, "sources", source.Key)

		fetcher, err := fetcher.New(source, fetcherDataPath, logger)
		if err != nil {
			logger.Warn("error creating fetcher", "error", err)

			continue
		}

		files, updated, err := fetcher.FetchIfNeeded(ctx)

		if err != nil {
			var exerr *exec.ExitError
			if errors.As(err, &exerr) {
				lines := strings.Split(strings.TrimSpace(string(exerr.Stderr)), "\n")
				for _, line := range lines {
					logger.Warn("importer fetch failed", "stderr", line, "status", exerr.ExitCode())
				}
			} else {
				logger.Warn("importer fetch failed", "error", err)
			}

			continue
		}
		logger.Info("importer fetch succeeded", "updated", updated)

		if updated || forceUpdate {
			err = setRepoRevision(files.Revision, source)
			if err != nil {
				logger.Warn("could not set source repo revision", "error", err)
			}

			var file io.ReadCloser
			var processor Processor
			switch source.Importer {
			case config.Options:
				logger.Debug(
					"creating processor",
					"filename",
					files.Options,
					"revision",
					source.Repo.Revision,
				)
				file, err = openFileDecoded(files.Options)
				if err != nil {
					logger.Warn("could not open file", "filename", files.Options, "error", err)
				}
				processor, err = NewOptionProcessor(file, source)
			case config.Packages:
				logger.Debug(
					"creating processor",
					"filename",
					files.Packages,
					"revision",
					source.Repo.Revision,
				)
				file, err = openFileDecoded(files.Packages)
				if err != nil {
					logger.Warn("could not open file", "filename", files.Packages, "error", err)
				}
				processor, err = NewPackageProcessor(file, source)
			}
			if err != nil {
				logger.Warn("failed to create processor", "type", source.Importer, "error", err)
			}

			hadWarnings := process(ctx, indexer, processor, logger)

			if hadWarnings {
				logger.Warn("importer succeeded, but with warnings/errors")
			} else {
				logger.Info("importer succeeded")
			}
		}
	}

	return nil
}