Skip to content

Commit ffc2b16

Browse files
committed
cue/cmd: teach cue fmt to not overwrite formatted files
Currently, `cue fmt` will always write to cue files, regardless of whether they are formatted or not. In the case of formatted files, their contents will remain exactly the same, but their modification time will change. This is problematic for tools that rely on the mod time. To fix this, we buffer the original/formatted bytes. If the result is exactly the same, the file is not written to. Fixes #1731, #363 Signed-off-by: Noam Dolovich <[email protected]> Change-Id: Iea55c1eb9e99c1dcba301194857a92dad1faa9ad
1 parent 33050e2 commit ffc2b16

File tree

3 files changed

+56
-25
lines changed

3 files changed

+56
-25
lines changed

cmd/cue/cmd/fmt.go

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,14 @@ func newFmtCmd(c *Command) *cobra.Command {
8888
// When using --check and --diff, we need to buffer the input and output bytes to compare them.
8989
var original []byte
9090
var formatted bytes.Buffer
91-
if doDiff || check {
92-
if bs, ok := file.Source.([]byte); ok {
93-
original = bs
94-
} else {
95-
original, err = source.ReadAll(file.Filename, file.Source)
96-
exitOnErr(cmd, err, true)
97-
file.Source = original
98-
}
99-
cfg.Out = &formatted
91+
if bs, ok := file.Source.([]byte); ok {
92+
original = bs
93+
} else {
94+
original, err = source.ReadAll(file.Filename, file.Source)
95+
exitOnErr(cmd, err, true)
96+
file.Source = original
10097
}
98+
cfg.Out = &formatted
10199

102100
var files []*ast.File
103101
d := encoding.NewDecoder(cmd.ctx, file, &cfg)
@@ -130,24 +128,35 @@ func newFmtCmd(c *Command) *cobra.Command {
130128
exitOnErr(cmd, err, true)
131129
}
132130

133-
if (doDiff || check) && !bytes.Equal(formatted.Bytes(), original) {
134-
foundBadlyFormatted = true
135-
var path string
136-
if file.Filename != "-" {
137-
f := file.Filename
138-
path, err = filepath.Rel(cwd, f)
139-
if err != nil {
140-
path = f
141-
}
142-
} else {
143-
path = "<standard input>"
131+
if bytes.Equal(formatted.Bytes(), original) {
132+
continue
133+
}
134+
135+
foundBadlyFormatted = true
136+
var path string
137+
if file.Filename != "-" {
138+
f := file.Filename
139+
path, err = filepath.Rel(cwd, f)
140+
if err != nil {
141+
path = f
144142
}
143+
} else {
144+
path = "<standard input>"
145+
}
145146

146-
if doDiff {
147-
d := diff.Diff(path+".orig", original, path, formatted.Bytes())
148-
fmt.Fprintln(stdout, string(d))
149-
} else {
150-
fmt.Fprintln(stdout, path)
147+
switch {
148+
case doDiff:
149+
d := diff.Diff(path+".orig", original, path, formatted.Bytes())
150+
fmt.Fprintln(stdout, string(d))
151+
case check:
152+
fmt.Fprintln(stdout, path)
153+
case file.Filename == "-":
154+
if _, err := fmt.Fprint(stdout, formatted.String()); err != nil {
155+
exitOnErr(cmd, err, false)
156+
}
157+
default:
158+
if err := os.WriteFile(file.Filename, formatted.Bytes(), 0644); err != nil {
159+
exitOnErr(cmd, err, false)
151160
}
152161
}
153162
}

cmd/cue/cmd/script_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,18 @@ func TestScript(t *testing.T) {
116116
ts.Check(os.WriteFile(path, []byte(data), 0o666))
117117
}
118118
},
119+
// mod-time prints the modification time of a file to stdout.
120+
// The time is displayed as nanoseconds since the Unix epoch.
121+
"mod-time": func(ts *testscript.TestScript, neg bool, args []string) {
122+
if neg || len(args) != 1 {
123+
ts.Fatalf("usage: mod-time PATH")
124+
}
125+
path := ts.MkAbs(args[0])
126+
fi, err := os.Stat(path)
127+
ts.Check(err)
128+
_, err = fmt.Fprint(ts.Stdout(), fi.ModTime().UnixNano())
129+
ts.Check(err)
130+
},
119131
// get-manifest writes the manifest for a given reference within an OCI
120132
// registry to a file in JSON format.
121133
"get-manifest": func(ts *testscript.TestScript, neg bool, args []string) {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Verify that cue fmt does not write to files
2+
# if they are already formatted
3+
mod-time formatted.cue
4+
cp stdout mod-time.txt
5+
exec cue fmt formatted.cue
6+
mod-time formatted.cue
7+
cmp stdout mod-time.txt
8+
9+
-- formatted.cue --
10+
a: 1

0 commit comments

Comments
 (0)