Skip to content

Commit 23e563b

Browse files
authored
Merge pull request #344 from numtide/fix/no-cache
fix: --no-cache
2 parents be50beb + 42decbf commit 23e563b

File tree

5 files changed

+143
-98
lines changed

5 files changed

+143
-98
lines changed

cache/cache.go

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -283,21 +283,12 @@ func Update(files []*walk.File) error {
283283
bucket := tx.Bucket([]byte(pathsBucket))
284284

285285
for _, f := range files {
286-
currentInfo, err := os.Stat(f.Path)
287-
if err != nil {
288-
return err
289-
}
290-
291-
if !(f.Info.ModTime() == currentInfo.ModTime() && f.Info.Size() == currentInfo.Size()) {
292-
stats.Add(stats.Formatted, 1)
293-
}
294-
295286
entry := Entry{
296-
Size: currentInfo.Size(),
297-
Modified: currentInfo.ModTime(),
287+
Size: f.Info.Size(),
288+
Modified: f.Info.ModTime(),
298289
}
299290

300-
if err = putEntry(bucket, f.RelPath, &entry); err != nil {
291+
if err := putEntry(bucket, f.RelPath, &entry); err != nil {
301292
return err
302293
}
303294
}

cli/cli.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ type Format struct {
4141
globalExcludes []glob.Glob
4242

4343
filesCh chan *walk.File
44+
formattedCh chan *walk.File
4445
processedCh chan *walk.File
4546
}
4647

cli/format.go

Lines changed: 133 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -148,97 +148,22 @@ func (f *Format) Run() (err error) {
148148
// we use a multiple of batch size here as a rudimentary concurrency optimization based on the host machine
149149
f.filesCh = make(chan *walk.File, BatchSize*runtime.NumCPU())
150150

151+
// create a channel for files that have been formatted
152+
f.formattedCh = make(chan *walk.File, cap(f.filesCh))
153+
151154
// create a channel for files that have been processed
152155
f.processedCh = make(chan *walk.File, cap(f.filesCh))
153156

154157
// start concurrent processing tasks in reverse order
155-
if !f.NoCache {
156-
eg.Go(f.updateCache(ctx))
157-
}
158+
eg.Go(f.updateCache(ctx))
159+
eg.Go(f.detectFormatted(ctx))
158160
eg.Go(f.applyFormatters(ctx))
159161
eg.Go(f.walkFilesystem(ctx))
160162

161163
// wait for everything to complete
162164
return eg.Wait()
163165
}
164166

165-
func (f *Format) updateCache(ctx context.Context) func() error {
166-
return func() error {
167-
// used to batch updates for more efficient txs
168-
batch := make([]*walk.File, 0, BatchSize)
169-
170-
// apply a batch
171-
processBatch := func() error {
172-
if f.Stdin {
173-
// do nothing
174-
return nil
175-
}
176-
if err := cache.Update(batch); err != nil {
177-
return err
178-
}
179-
batch = batch[:0]
180-
return nil
181-
}
182-
183-
LOOP:
184-
for {
185-
select {
186-
// detect ctx cancellation
187-
case <-ctx.Done():
188-
return ctx.Err()
189-
// respond to processed files
190-
case file, ok := <-f.processedCh:
191-
if !ok {
192-
// channel has been closed, no further files to process
193-
break LOOP
194-
}
195-
196-
if f.Stdin {
197-
// dump file into stdout
198-
f, err := os.Open(file.Path)
199-
if err != nil {
200-
return fmt.Errorf("failed to open %s: %w", file.Path, err)
201-
}
202-
if _, err = io.Copy(os.Stdout, f); err != nil {
203-
return fmt.Errorf("failed to copy %s to stdout: %w", file.Path, err)
204-
}
205-
if err = os.Remove(f.Name()); err != nil {
206-
return fmt.Errorf("failed to remove temp file %s: %w", file.Path, err)
207-
}
208-
209-
stats.Add(stats.Formatted, 1)
210-
continue
211-
}
212-
213-
// append to batch and process if we have enough
214-
batch = append(batch, file)
215-
if len(batch) == BatchSize {
216-
if err := processBatch(); err != nil {
217-
return err
218-
}
219-
}
220-
}
221-
}
222-
223-
// final flush
224-
if err := processBatch(); err != nil {
225-
return err
226-
}
227-
228-
// if fail on change has been enabled, check that no files were actually formatted, throwing an error if so
229-
if f.FailOnChange && stats.Value(stats.Formatted) != 0 {
230-
return ErrFailOnChange
231-
}
232-
233-
// print stats to stdout unless we are processing stdin and printing the results to stdout
234-
if !f.Stdin {
235-
stats.Print()
236-
}
237-
238-
return nil
239-
}
240-
}
241-
242167
func (f *Format) walkFilesystem(ctx context.Context) func() error {
243168
return func() error {
244169
eg, ctx := errgroup.WithContext(ctx)
@@ -368,9 +293,9 @@ func (f *Format) applyFormatters(ctx context.Context) func() error {
368293
}
369294
}
370295

371-
// pass each file to the processed channel
296+
// pass each file to the formatted channel
372297
for _, task := range tasks {
373-
f.processedCh <- task.File
298+
f.formattedCh <- task.File
374299
}
375300

376301
return nil
@@ -392,7 +317,7 @@ func (f *Format) applyFormatters(ctx context.Context) func() error {
392317
return func() error {
393318
defer func() {
394319
// close processed channel
395-
close(f.processedCh)
320+
close(f.formattedCh)
396321
}()
397322

398323
// iterate the files channel
@@ -402,7 +327,7 @@ func (f *Format) applyFormatters(ctx context.Context) func() error {
402327
if format.PathMatches(file.RelPath, f.globalExcludes) {
403328
log.Debugf("path matched global excludes: %s", file.RelPath)
404329
// mark it as processed and continue to the next
405-
f.processedCh <- file
330+
f.formattedCh <- file
406331
continue
407332
}
408333

@@ -421,7 +346,7 @@ func (f *Format) applyFormatters(ctx context.Context) func() error {
421346
}
422347
log.Logf(f.OnUnmatched, "no formatter for path: %s", file.RelPath)
423348
// mark it as processed and continue to the next
424-
f.processedCh <- file
349+
f.formattedCh <- file
425350
} else {
426351
// record the match
427352
stats.Add(stats.Matched, 1)
@@ -444,6 +369,129 @@ func (f *Format) applyFormatters(ctx context.Context) func() error {
444369
}
445370
}
446371

372+
func (f *Format) detectFormatted(ctx context.Context) func() error {
373+
return func() error {
374+
defer func() {
375+
// close formatted channel
376+
close(f.processedCh)
377+
}()
378+
379+
for {
380+
select {
381+
382+
// detect ctx cancellation
383+
case <-ctx.Done():
384+
return ctx.Err()
385+
// take the next file that has been processed
386+
case file, ok := <-f.formattedCh:
387+
if !ok {
388+
// channel has been closed, no further files to process
389+
return nil
390+
}
391+
392+
// look up current file info
393+
currentInfo, err := os.Stat(file.Path)
394+
if err != nil {
395+
return fmt.Errorf("failed to stat processed file: %w", err)
396+
}
397+
398+
// check if the file has changed
399+
if !(file.Info.ModTime() == currentInfo.ModTime() && file.Info.Size() == currentInfo.Size()) {
400+
// record the change
401+
stats.Add(stats.Formatted, 1)
402+
// log the change for diagnostics
403+
log.Debugf("file has been changed: %s", file.Path)
404+
// update the file info
405+
file.Info = currentInfo
406+
}
407+
408+
// mark as processed
409+
f.processedCh <- file
410+
}
411+
}
412+
}
413+
}
414+
415+
func (f *Format) updateCache(ctx context.Context) func() error {
416+
return func() error {
417+
// used to batch updates for more efficient txs
418+
batch := make([]*walk.File, 0, BatchSize)
419+
420+
// apply a batch
421+
processBatch := func() error {
422+
// pass the batch to the cache for updating
423+
if err := cache.Update(batch); err != nil {
424+
return err
425+
}
426+
batch = batch[:0]
427+
return nil
428+
}
429+
430+
// if we are processing from stdin that means we are outputting to stdout, no caching involved
431+
// if f.NoCache is set that means either the user explicitly disabled the cache or we failed to open on
432+
if f.Stdin || f.NoCache {
433+
// do nothing
434+
processBatch = func() error { return nil }
435+
}
436+
437+
LOOP:
438+
for {
439+
select {
440+
// detect ctx cancellation
441+
case <-ctx.Done():
442+
return ctx.Err()
443+
// respond to formatted files
444+
case file, ok := <-f.processedCh:
445+
if !ok {
446+
// channel has been closed, no further files to process
447+
break LOOP
448+
}
449+
450+
if f.Stdin {
451+
// dump file into stdout
452+
f, err := os.Open(file.Path)
453+
if err != nil {
454+
return fmt.Errorf("failed to open %s: %w", file.Path, err)
455+
}
456+
if _, err = io.Copy(os.Stdout, f); err != nil {
457+
return fmt.Errorf("failed to copy %s to stdout: %w", file.Path, err)
458+
}
459+
if err = os.Remove(f.Name()); err != nil {
460+
return fmt.Errorf("failed to remove temp file %s: %w", file.Path, err)
461+
}
462+
463+
continue
464+
}
465+
466+
// append to batch and process if we have enough
467+
batch = append(batch, file)
468+
if len(batch) == BatchSize {
469+
if err := processBatch(); err != nil {
470+
return err
471+
}
472+
}
473+
}
474+
}
475+
476+
// final flush
477+
if err := processBatch(); err != nil {
478+
return err
479+
}
480+
481+
// if fail on change has been enabled, check that no files were actually formatted, throwing an error if so
482+
if f.FailOnChange && stats.Value(stats.Formatted) != 0 {
483+
return ErrFailOnChange
484+
}
485+
486+
// print stats to stdout unless we are processing stdin and printing the results to stdout
487+
if !f.Stdin {
488+
stats.Print()
489+
}
490+
491+
return nil
492+
}
493+
}
494+
447495
func findUp(searchDir string, fileName string) (path string, dir string, err error) {
448496
for _, dir := range eachDir(searchDir) {
449497
path := filepath.Join(dir, fileName)

cli/format_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,11 @@ func TestFailOnChange(t *testing.T) {
364364
test.WriteConfig(t, configPath, cfg)
365365
_, err := cmd(t, "--fail-on-change", "--config-file", configPath, "--tree-root", tempDir)
366366
as.ErrorIs(err, ErrFailOnChange)
367+
368+
// test with no cache
369+
test.WriteConfig(t, configPath, cfg)
370+
_, err = cmd(t, "--fail-on-change", "--config-file", configPath, "--tree-root", tempDir, "--no-cache")
371+
as.ErrorIs(err, ErrFailOnChange)
367372
}
368373

369374
func TestBustCacheOnFormatterChange(t *testing.T) {

format/formatter.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func (f *Formatter) Apply(ctx context.Context, tasks []*Task) error {
8282

8383
//
8484

85-
f.log.Infof("%v files processed in %v", len(tasks), time.Since(start))
85+
f.log.Infof("%v file(s) processed in %v", len(tasks), time.Since(start))
8686

8787
return nil
8888
}

0 commit comments

Comments
 (0)