diff --git a/WORKSPACE b/WORKSPACE index b27a042..6b820a8 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -104,10 +104,10 @@ oci_pull( oci_pull( name = "runner_base", - digest = "sha256:d0b2922dc48cb6acb7c767f89f0c92ccbe1a043166971bac0b585b3851a9b720", - # TODO(#44): Replace this base image with a more permanent one. - image = "docker.io/curfewreplica/pactatest", - # platforms = ["linux/amd64"], + # This digest is of the nightly/main tag as of 2024-07-22 + digest = "sha256:7adec544294b5cb9e11c6bb4c43d0b2de646e5f933639f86c85f3f03c99f650e", + image = "ghcr.io/rmi-pacta/workflow.pacta.webapp", + platforms = ["linux/amd64"], ) oci_pull( diff --git a/async/async.go b/async/async.go index 8a5cc8b..100edc7 100644 --- a/async/async.go +++ b/async/async.go @@ -30,6 +30,9 @@ type Config struct { Blob Blob PubSub *publisher.Client Logger *zap.Logger + + BenchmarkDir string + PACTADataDir string } func (c *Config) validate() error { @@ -43,6 +46,12 @@ func (c *Config) validate() error { return errors.New("no logger given") } + if c.BenchmarkDir == "" { + return errors.New("no benchmark dir specified") + } + if c.PACTADataDir == "" { + return errors.New("no PACTA data dir specified") + } return nil } @@ -56,6 +65,9 @@ type Handler struct { blob Blob pubsub *publisher.Client logger *zap.Logger + + // Mounted directories with data needed for report generation. + benchmarkDir, pactaDataDir string } func New(cfg *Config) (*Handler, error) { @@ -64,9 +76,11 @@ func New(cfg *Config) (*Handler, error) { } return &Handler{ - blob: cfg.Blob, - pubsub: cfg.PubSub, - logger: cfg.Logger, + blob: cfg.Blob, + pubsub: cfg.PubSub, + logger: cfg.Logger, + benchmarkDir: cfg.BenchmarkDir, + pactaDataDir: cfg.PACTADataDir, }, nil } @@ -221,31 +235,169 @@ func (h *Handler) CreateAudit(ctx context.Context, taskID task.ID, req *task.Cre return errors.New("not implemented") } -func (h *Handler) CreateReport(ctx context.Context, taskID task.ID, req *task.CreateReportRequest, reportContainer string) error { - fileNames := []string{} - for _, blobURI := range req.BlobURIs { - // Load the parsed portfolio from blob storage, place it in /mnt/ - // processed_portfolios, where the `create_report.R` script expects it - // to be. - fileNameWithExt := filepath.Base(string(blobURI)) - if !strings.HasSuffix(fileNameWithExt, ".json") { - return fmt.Errorf("given blob wasn't a JSON-formatted portfolio, %q", fileNameWithExt) +type ReportInput struct { + Portfolio ReportInputPortfolio `json:"portfolio"` + Inherit string `json:"inherit"` +} + +type ReportInputPortfolio struct { + Files string `json:"files"` + HoldingsDate string `json:"holdingsDate"` + Name string `json:"name"` +} + +type ReportEnv struct { + rootDir string + + // These are mounted in from externally. + benchmarksDir string + pactaDataDir string +} + +func initReportEnv(benchmarkDir, pactaDataDir, baseDir string) (*ReportEnv, error) { + // Make sure the base directory exists first. + if err := os.MkdirAll(baseDir, 0700); err != nil { + return nil, fmt.Errorf("failed to create base input dir: %w", err) + } + // We create temp subdirectories, because while this code currently executes in + // a new container for each invocation, that might not always be the case. + rootDir, err := os.MkdirTemp(baseDir, "create-report") + if err != nil { + return nil, fmt.Errorf("failed to create temp dir for input CSVs: %w", err) + } + + re := &ReportEnv{ + rootDir: rootDir, + benchmarksDir: benchmarkDir, + pactaDataDir: pactaDataDir, + } + + if err := re.makeDirectories(); err != nil { + return nil, fmt.Errorf("failed to create directories: %w", err) + } + + return re, nil +} + +type ReportDir string + +const ( + PortfoliosDir = ReportDir("portfolios") + RealEstateDir = ReportDir("real-estate") + ScoreCardDir = ReportDir("score-card") + SurveyDir = ReportDir("survey") + + // Outputs + AnalysisOutputDir = ReportDir("analysis-output") + ReportOutputDir = ReportDir("report-output") + SummaryOutputDir = ReportDir("summary-output") +) + +func (r *ReportEnv) outputDirs() []string { + return []string{ + r.pathForDir(AnalysisOutputDir), + r.pathForDir(ReportOutputDir), + r.pathForDir(SummaryOutputDir), + } +} + +func (r *ReportEnv) asEnvVars() []string { + return []string{ + "BENCHMARKS_DIR=" + r.benchmarksDir, + "PACTA_DATA_DIR=" + r.pactaDataDir, + "PORTFOLIO_DIR=" + r.pathForDir(PortfoliosDir), + "REAL_ESTATE_DIR=" + r.pathForDir(RealEstateDir), + "SCORE_CARD_DIR=" + r.pathForDir(ScoreCardDir), + "SURVEY_DIR=" + r.pathForDir(SurveyDir), + "ANALYSIS_OUTPUT_DIR=" + r.pathForDir(AnalysisOutputDir), + "REPORT_OUTPUT_DIR=" + r.pathForDir(ReportOutputDir), + "SUMMARY_OUTPUT_DIR=" + r.pathForDir(SummaryOutputDir), + } +} + +func (r *ReportEnv) pathForDir(d ReportDir) string { + return filepath.Join(r.rootDir, string(d)) +} + +func (r *ReportEnv) makeDirectories() error { + var rErr error + makeDir := func(reportDir ReportDir) { + if rErr != nil { + return } - fileNames = append(fileNames, strings.TrimSuffix(fileNameWithExt, ".json")) - destPath := filepath.Join("/", "mnt", "processed_portfolios", fileNameWithExt) - if err := h.downloadBlob(ctx, string(blobURI), destPath); err != nil { - return fmt.Errorf("failed to download processed portfolio blob: %w", err) + dir := r.pathForDir(reportDir) + if err := os.Mkdir(dir, 0700); err != nil { + rErr = fmt.Errorf("failed to create dir %q: %w", dir, err) + return } } - reportDir := filepath.Join("/", "mnt", "reports") - if err := os.MkdirAll(reportDir, 0600); err != nil { - return fmt.Errorf("failed to create directory for reports to get copied to: %w", err) + // Inputs + makeDir(PortfoliosDir) + makeDir(RealEstateDir) // Used as part of specific projects, empty for now. + makeDir(ScoreCardDir) // Used as part of specific projects, empty for now. + makeDir(SurveyDir) // Used as part of specific projects, empty for now. + + // Outputs + makeDir(AnalysisOutputDir) + makeDir(ReportOutputDir) + makeDir(SummaryOutputDir) + + if rErr != nil { + return rErr + } + return nil +} + +func (h *Handler) CreateReport(ctx context.Context, taskID task.ID, req *task.CreateReportRequest, reportContainer string) error { + if n := len(req.BlobURIs); n != 1 { + return fmt.Errorf("expected exactly one blob URI as input, got %d", n) + } + blobURI := req.BlobURIs[0] + + // We use this instead of /mnt/... because the base image (quite + // reasonably) uses a non-root user, so we can't be creating directories in the + // root filesystem all willy nilly. + baseDir := filepath.Join("/", "home", "workflow-pacta-webapp") + + reportEnv, err := initReportEnv(h.benchmarkDir, h.pactaDataDir, baseDir) + if err != nil { + return fmt.Errorf("failed to init report env: %w", err) + } + + // Load the parsed portfolio from blob storage, place it in our PORFOLIO_DIR, + // where the `run_pacta.R` script expects it to be. + fileNameWithExt := filepath.Base(string(blobURI)) + if !strings.HasSuffix(fileNameWithExt, ".csv") { + return fmt.Errorf("given blob wasn't a CSV-formatted portfolio, %q", fileNameWithExt) + } + destPath := filepath.Join(reportEnv.pathForDir(PortfoliosDir), fileNameWithExt) + if err := h.downloadBlob(ctx, string(blobURI), destPath); err != nil { + return fmt.Errorf("failed to download processed portfolio blob: %w", err) + } + + inp := ReportInput{ + Portfolio: ReportInputPortfolio{ + Files: fileNameWithExt, + HoldingsDate: "2023-12-31", // TODO(#206) + Name: "FooPortfolio", // TODO(#206) + }, + Inherit: "GENERAL_2023Q4", // TODO(#206): Should this be configurable } - cmd := exec.CommandContext(ctx, "/usr/local/bin/Rscript", "/app/create_report.R") + var inpJSON bytes.Buffer + if err := json.NewEncoder(&inpJSON).Encode(inp); err != nil { + return fmt.Errorf("failed to encode report input as JSON: %w", err) + } + + cmd := exec.CommandContext(ctx, + "/usr/local/bin/Rscript", + "--vanilla", "/workflow.pacta.webapp/inst/extdata/scripts/run_pacta.R", + inpJSON.String()) + + cmd.Env = append(cmd.Env, reportEnv.asEnvVars()...) cmd.Env = append(cmd.Env, - "PORTFOLIO="+strings.Join(fileNames, ","), + "LOG_LEVEL=DEBUG", "HOME=/root", /* Required by pandoc */ ) cmd.Stdout = os.Stdout @@ -255,23 +407,20 @@ func (h *Handler) CreateReport(ctx context.Context, taskID task.ID, req *task.Cr return fmt.Errorf("failed to run pacta test CLI: %w", err) } - // Download outputs from from /out and upload them to Azure - dirEntries, err := os.ReadDir(reportDir) - if err != nil { - return fmt.Errorf("failed to read report directory: %w", err) - } - var artifacts []*task.AnalysisArtifact - for _, dirEntry := range dirEntries { - if !dirEntry.IsDir() { - continue - } - dirPath := filepath.Join(reportDir, dirEntry.Name()) - tmp, err := h.uploadDirectory(ctx, dirPath, reportContainer) + uploadDir := func(dir string) error { + aas, err := h.uploadDirectory(ctx, dir, reportContainer, req.AnalysisID) if err != nil { return fmt.Errorf("failed to upload report directory: %w", err) } - artifacts = tmp + artifacts = append(artifacts, aas...) + return nil + } + + for _, outDir := range reportEnv.outputDirs() { + if err := uploadDir(outDir); err != nil { + return fmt.Errorf("failed to upload artifacts %q: %w", outDir, err) + } } events := []publisher.Event{ @@ -331,7 +480,7 @@ func (h *Handler) downloadBlob(ctx context.Context, srcURI, destPath string) err return nil } -func (h *Handler) uploadDirectory(ctx context.Context, dirPath, container string) ([]*task.AnalysisArtifact, error) { +func (h *Handler) uploadDirectory(ctx context.Context, dirPath, container string, analysisID pacta.AnalysisID) ([]*task.AnalysisArtifact, error) { base := filepath.Base(dirPath) var artifacts []*task.AnalysisArtifact @@ -341,14 +490,14 @@ func (h *Handler) uploadDirectory(ctx context.Context, dirPath, container string } // This is a file, let's upload it to the container - uri := blob.Join(h.blob.Scheme(), container, base, strings.TrimPrefix(path, dirPath+"/")) + uri := blob.Join(h.blob.Scheme(), container, string(analysisID), base, strings.TrimPrefix(path, dirPath+"/")) if err := h.uploadBlob(ctx, path, uri); err != nil { return fmt.Errorf("failed to upload blob: %w", err) } fn := filepath.Base(path) // Returns pacta.FileType_UNKNOWN for unrecognized extensions, which we'll serve as binary blobs. - ft := fileTypeFromExt(filepath.Ext(fn)) + ft := fileTypeFromFilename(fn) if ft == pacta.FileType_UNKNOWN { h.logger.Error("unhandled file extension", zap.String("dir", dirPath), zap.String("file_ext", filepath.Ext(fn))) } @@ -365,7 +514,9 @@ func (h *Handler) uploadDirectory(ctx context.Context, dirPath, container string return artifacts, nil } -func fileTypeFromExt(ext string) pacta.FileType { +func fileTypeFromFilename(fn string) pacta.FileType { + ext := filepath.Ext(fn) + switch ext { case ".csv": return pacta.FileType_CSV @@ -383,8 +534,29 @@ func fileTypeFromExt(ext string) pacta.FileType { return pacta.FileType_CSS case ".js": return pacta.FileType_JS + case ".map": + switch ext2 := filepath.Ext(strings.TrimSuffix(fn, ext)); ext2 { + case ".js": + return pacta.FileType_JS_MAP + default: + return pacta.FileType_UNKNOWN + } case ".ttf": return pacta.FileType_TTF + case ".woff": + return pacta.FileType_WOFF + case ".woff2": + return pacta.FileType_WOFF2 + case ".eot": + return pacta.FileType_EOT + case ".svg": + return pacta.FileType_SVG + case ".png": + return pacta.FileType_PNG + case ".jpg": + return pacta.FileType_JPG + case ".pdf": + return pacta.FileType_TTF default: return pacta.FileType_UNKNOWN } diff --git a/cmd/runner/configs/local.conf b/cmd/runner/configs/local.conf index 9a73adf..27cf204 100644 --- a/cmd/runner/configs/local.conf +++ b/cmd/runner/configs/local.conf @@ -6,3 +6,6 @@ azure_topic_location centralus-1 azure_storage_account rmipactalocal azure_report_container reports + +benchmark_dir /mnt/workflow-data/benchmarks/2023Q4_20240529T002355Z +pacta_data_dir /mnt/workflow-data/pacta-data/2023Q4_20240424T120055Z diff --git a/cmd/runner/main.go b/cmd/runner/main.go index 664e029..2a3e45d 100644 --- a/cmd/runner/main.go +++ b/cmd/runner/main.go @@ -37,6 +37,9 @@ func run(args []string) error { var ( env = fs.String("env", "", "The environment we're running in.") + benchmarkDir = fs.String("benchmark_dir", "", "The path to the benchmark data for report generation") + pactaDataDir = fs.String("pacta_data_dir", "", "The path to the PACTA data for report generation") + azEventTopic = fs.String("azure_event_topic", "", "The EventGrid topic to send notifications when tasks have finished") azTopicLocation = fs.String("azure_topic_location", "", "The location (like 'centralus-1') where our EventGrid topics are hosted") @@ -80,9 +83,11 @@ func run(args []string) error { } h, err := async.New(&async.Config{ - Blob: blobClient, - PubSub: pubsubClient, - Logger: logger, + Blob: blobClient, + PubSub: pubsubClient, + Logger: logger, + BenchmarkDir: *benchmarkDir, + PACTADataDir: *pactaDataDir, }) if err != nil { return fmt.Errorf("failed to init async biz logic handler: %w", err) diff --git a/cmd/server/main.go b/cmd/server/main.go index 81eb8e1..b815c74 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -73,6 +73,7 @@ func run(args []string) error { localDockerTenantID = fs.String("local_docker_tenant_id", "", "The Azure Tenant ID the localdocker service principal lives in") localDockerClientID = fs.String("local_docker_client_id", "", "The client ID of the localdocker service principal") localDockerClientSecret = fs.String("local_docker_client_secret", "", "The client secret for accessing the localdocker service principal") + dockerRepoRoot = fs.String("docker_repo_root", "", "Absolute path to the repo, used for finding the ./workflow-data directory for the runner") // PACTA Execution useAZRunner = fs.Bool("use_azure_runner", false, "If true, execute PACTA on Azure Container Apps Jobs instead of a local instance.") @@ -220,7 +221,7 @@ func run(args []string) error { TenantID: *localDockerTenantID, ClientID: *localDockerClientID, ClientSecret: *localDockerClientSecret, - }) + }, *dockerRepoRoot) if err != nil { return fmt.Errorf("failed to init docker runner: %w", err) } diff --git a/db/sqldb/golden/human_readable_schema.sql b/db/sqldb/golden/human_readable_schema.sql index ca375e4..3552780 100644 --- a/db/sqldb/golden/human_readable_schema.sql +++ b/db/sqldb/golden/human_readable_schema.sql @@ -52,7 +52,15 @@ CREATE TYPE file_type AS ENUM ( 'css', 'js', 'ttf', - 'unknown'); + 'unknown', + 'js.map', + 'woff', + 'woff2', + 'eot', + 'svg', + 'png', + 'jpg', + 'pdf'); CREATE TYPE language AS ENUM ( 'en', 'de', diff --git a/db/sqldb/golden/schema_dump.sql b/db/sqldb/golden/schema_dump.sql index 1494fca..b779f6f 100644 --- a/db/sqldb/golden/schema_dump.sql +++ b/db/sqldb/golden/schema_dump.sql @@ -135,7 +135,15 @@ CREATE TYPE public.file_type AS ENUM ( 'css', 'js', 'ttf', - 'unknown' + 'unknown', + 'js.map', + 'woff', + 'woff2', + 'eot', + 'svg', + 'png', + 'jpg', + 'pdf' ); diff --git a/db/sqldb/migrations/0014_add_more_report_file_types.down.sql b/db/sqldb/migrations/0014_add_more_report_file_types.down.sql new file mode 100644 index 0000000..e2ec2d3 --- /dev/null +++ b/db/sqldb/migrations/0014_add_more_report_file_types.down.sql @@ -0,0 +1,25 @@ +BEGIN; + +-- There isn't a way to delete a value from an enum, so this is the workaround +-- https://stackoverflow.com/a/56777227/17909149 + +ALTER TABLE blob ALTER file_type TYPE TEXT; + +DROP TYPE file_type; +CREATE TYPE file_type AS ENUM ( + 'csv', + 'yaml', + 'zip', + 'html', + 'json', + 'txt', + 'css', + 'js', + 'ttf', + 'unknown'); + +ALTER TABLE blob + ALTER file_type TYPE file_type + USING file_type::file_type; + +COMMIT; diff --git a/db/sqldb/migrations/0014_add_more_report_file_types.up.sql b/db/sqldb/migrations/0014_add_more_report_file_types.up.sql new file mode 100644 index 0000000..786ead7 --- /dev/null +++ b/db/sqldb/migrations/0014_add_more_report_file_types.up.sql @@ -0,0 +1,12 @@ +BEGIN; + +ALTER TYPE file_type ADD VALUE 'js.map'; +ALTER TYPE file_type ADD VALUE 'woff'; +ALTER TYPE file_type ADD VALUE 'woff2'; +ALTER TYPE file_type ADD VALUE 'eot'; +ALTER TYPE file_type ADD VALUE 'svg'; +ALTER TYPE file_type ADD VALUE 'png'; +ALTER TYPE file_type ADD VALUE 'jpg'; +ALTER TYPE file_type ADD VALUE 'pdf'; + +COMMIT; diff --git a/db/sqldb/sqldb_test.go b/db/sqldb/sqldb_test.go index 8c0f576..bd2f946 100644 --- a/db/sqldb/sqldb_test.go +++ b/db/sqldb/sqldb_test.go @@ -95,6 +95,7 @@ func TestSchemaHistory(t *testing.T) { {ID: 11, Version: 11}, // 0011_add_report_file_types {ID: 12, Version: 12}, // 0012_portfolio_properties {ID: 13, Version: 13}, // 0013_user_search + {ID: 14, Version: 14}, // 0014_add_more_report_file_types } if diff := cmp.Diff(want, got); diff != "" { diff --git a/dockertask/dockertask.go b/dockertask/dockertask.go index 8961095..c6f6dee 100644 --- a/dockertask/dockertask.go +++ b/dockertask/dockertask.go @@ -7,6 +7,7 @@ package dockertask import ( "context" "fmt" + "path/filepath" "github.com/RMI/pacta/task" "github.com/docker/docker/api/types" @@ -21,6 +22,8 @@ type Runner struct { client *client.Client logger *zap.Logger sp *ServicePrincipal + + repoRoot string } type ServicePrincipal struct { @@ -29,13 +32,13 @@ type ServicePrincipal struct { ClientSecret string } -func NewRunner(logger *zap.Logger, sp *ServicePrincipal) (*Runner, error) { +func NewRunner(logger *zap.Logger, sp *ServicePrincipal, repoRoot string) (*Runner, error) { cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { return nil, fmt.Errorf("failed to initialize Docker client: %w", err) } - return &Runner{client: cli, logger: logger, sp: sp}, nil + return &Runner{client: cli, logger: logger, sp: sp, repoRoot: repoRoot}, nil } func (r *Runner) Run(ctx context.Context, taskCfg *task.Config) (task.RunnerID, error) { @@ -69,6 +72,9 @@ func (r *Runner) Run(ctx context.Context, taskCfg *task.Config) (task.RunnerID, hostCfg := &container.HostConfig{ // AutoRemove: true, + Binds: []string{ + filepath.Join(r.repoRoot, "workflow-data") + ":/mnt/workflow-data:ro", + }, } resp, err := r.client.ContainerCreate(ctx, cfg, hostCfg, nil /* net config */, platform, "" /* random name */) diff --git a/pacta/pacta.go b/pacta/pacta.go index 276754b..54ca75f 100644 --- a/pacta/pacta.go +++ b/pacta/pacta.go @@ -208,10 +208,19 @@ const ( FileType_JSON = "json" // All for serving reports - FileType_TEXT = "txt" - FileType_CSS = "css" - FileType_JS = "js" - FileType_TTF = "ttf" + FileType_TEXT = "txt" + FileType_CSS = "css" + FileType_JS = "js" + FileType_JS_MAP = "js.map" + FileType_TTF = "ttf" + FileType_WOFF = "woff" + FileType_WOFF2 = "woff2" + FileType_EOT = "eot" + FileType_SVG = "svg" + FileType_PNG = "png" + FileType_JPG = "jpg" + FileType_PDF = "pdf" + FileType_UNKNOWN = "unknown" ) @@ -225,7 +234,15 @@ var FileTypeValues = []FileType{ FileType_TEXT, FileType_CSS, FileType_JS, + FileType_JS_MAP, FileType_TTF, + FileType_WOFF, + FileType_WOFF2, + FileType_EOT, + FileType_SVG, + FileType_PNG, + FileType_JPG, + FileType_PDF, FileType_UNKNOWN, } @@ -251,8 +268,24 @@ func ParseFileType(s string) (FileType, error) { return FileType_CSS, nil case "js": return FileType_JS, nil + case "js.map": + return FileType_JS_MAP, nil case "ttf": return FileType_TTF, nil + case "woff": + return FileType_WOFF, nil + case "woff2": + return FileType_WOFF2, nil + case "eot": + return FileType_EOT, nil + case "svg": + return FileType_SVG, nil + case "png": + return FileType_PNG, nil + case "jpg": + return FileType_JPG, nil + case "pdf": + return FileType_PDF, nil case "unknown": return FileType_UNKNOWN, nil } diff --git a/reportsrv/reportsrv.go b/reportsrv/reportsrv.go index b727638..f57c4c4 100644 --- a/reportsrv/reportsrv.go +++ b/reportsrv/reportsrv.go @@ -133,6 +133,7 @@ func (s *Server) serveReport(w http.ResponseWriter, r *http.Request) { if subPath == "" { subPath = "index.html" } + subPath = "report-output/report/" + subPath for _, aa := range artifacts { // Container is just 'reports', we can ignore that. diff --git a/reportsrv/reportsrv_test.go b/reportsrv/reportsrv_test.go index 0a941ab..8110494 100644 --- a/reportsrv/reportsrv_test.go +++ b/reportsrv/reportsrv_test.go @@ -69,13 +69,13 @@ func TestServeReport(t *testing.T) { env.db.blobs = map[pacta.BlobID]*pacta.Blob{ "blob.id1": &pacta.Blob{ ID: "blob.id1", - BlobURI: "test://reports/1111-2222-3333-4444/index.html", + BlobURI: "test://reports/1111-2222-3333-4444/report-output/report/index.html", FileType: pacta.FileType_HTML, FileName: "index.html", }, "blob.id2": &pacta.Blob{ ID: "blob.id2", - BlobURI: "test://reports/1111-2222-3333-4444/lib/some/package.js", + BlobURI: "test://reports/1111-2222-3333-4444/report-output/report/lib/some/package.js", FileType: pacta.FileType_JS, FileName: "package.js", }, @@ -84,8 +84,8 @@ func TestServeReport(t *testing.T) { htmlContent := "this is the index" jsContent := "function() { return 'some js' }" env.blob.blobContents = map[string]string{ - "test://reports/1111-2222-3333-4444/index.html": htmlContent, - "test://reports/1111-2222-3333-4444/lib/some/package.js": jsContent, + "test://reports/1111-2222-3333-4444/report-output/report/index.html": htmlContent, + "test://reports/1111-2222-3333-4444/report-output/report/lib/some/package.js": jsContent, } standardPath := "/report/" + aIDStr + "/" diff --git a/scripts/run_server.sh b/scripts/run_server.sh index c14bdcd..8b1c4bf 100755 --- a/scripts/run_server.sh +++ b/scripts/run_server.sh @@ -134,6 +134,7 @@ LOCAL_DSN+=" sslmode=disable" FLAGS+=( "--config=${ROOT}/cmd/server/configs/local.conf" "--local_dsn=${LOCAL_DSN}" + "--docker_repo_root=${ROOT}" ) if [[ ! -z "$EG_SUB_NAME" ]]; then