(ctx context.Context, logger slog.Logger, cfg *devConfig)
| 605 | } |
| 606 | |
| 607 | func develop(ctx context.Context, logger slog.Logger, cfg *devConfig) error { |
| 608 | sigCtx, stop := signal.NotifyContext(ctx, cli.StopSignals...) |
| 609 | defer stop() |
| 610 | |
| 611 | if err := preflight(sigCtx, logger, cfg); err != nil { |
| 612 | return err |
| 613 | } |
| 614 | |
| 615 | // Check the database before building. The mismatch check is |
| 616 | // a cheap file read; only starts temp postgres on actual |
| 617 | // mismatch. This avoids a wasted build cycle when the |
| 618 | // developer needs to re-run with --db-rollback or --db-reset. |
| 619 | if err := recoverDB(sigCtx, logger, cfg); err != nil { |
| 620 | return xerrors.Errorf("database recovery: %w", err) |
| 621 | } |
| 622 | |
| 623 | if err := buildBinary(sigCtx, logger, cfg); err != nil { |
| 624 | return xerrors.Errorf("build: %w", err) |
| 625 | } |
| 626 | |
| 627 | // Wrap in a cancelable context so deferred cleanup can |
| 628 | // trigger graceful shutdown on early return. |
| 629 | cancelCtx, cancelAll := context.WithCancel(sigCtx) |
| 630 | |
| 631 | group := newProcGroup(cancelCtx, logger) |
| 632 | defer func() { |
| 633 | cancelAll() |
| 634 | _ = group.Wait() |
| 635 | }() |
| 636 | |
| 637 | ctx = group.Ctx() |
| 638 | |
| 639 | if err := startServer(cfg, group); err != nil { |
| 640 | return err |
| 641 | } |
| 642 | |
| 643 | // The vite dev server proxies to the API and handles the |
| 644 | // case where the API isn't ready yet, so start it in parallel. |
| 645 | if err := group.Start("site", pnpmCmd(ctx, cfg)); err != nil { |
| 646 | return xerrors.Errorf("starting frontend: %w", err) |
| 647 | } |
| 648 | |
| 649 | apiURL := fmt.Sprintf("http://127.0.0.1:%d", cfg.apiPort) |
| 650 | if err := waitForHealthy(ctx, logger, apiURL); err != nil { |
| 651 | return err |
| 652 | } |
| 653 | |
| 654 | // Update migration tracking after the server has applied |
| 655 | // any new migrations. This keeps the cache current so the |
| 656 | // next run detects mismatches correctly. |
| 657 | if err := updateMigrationTracking(ctx, logger, cfg); err != nil { |
| 658 | logger.Warn(ctx, "failed to update migration tracking", |
| 659 | slog.Error(err)) |
| 660 | } |
| 661 | |
| 662 | if !cfg.skipSetup { |
| 663 | client, err := setupFirstUser(ctx, logger, cfg, apiURL) |
| 664 | if err != nil { |
no test coverage detected