mikefarah / yq

yq is a portable command-line YAML, JSON, XML, CSV, TOML and properties processor
https://mikefarah.gitbook.io/yq/
MIT License
11.94k stars 587 forks source link

Inplace flag overwrites symlinks with file contents when data file and /tmp are on the same partition #2155

Open sestegra opened 3 hours ago

sestegra commented 3 hours ago

Describe the bug The -i inplace flag overwrites symlinks with the file contents instead of modifying the symlinked file when data file and /tmp are on the same partition.

Version of yq: v4.44.3 Operating system: linux Installed via: binary release

Thanks to #1306 for helping me investigate the root cause.

Input Yaml data1.yml is on a the same filesystem partition as /tmp

data1.yml:

this: should really work
but: it strangely didn't

data2.yml: symlink to data1.yml

Command The command you ran:

yq -i '.components += "test"' data2.yml

Actual behavior

Before running yq command

$ ls -l | grep data
-rw-rw-r--  1 sestegra sestegra   67 Sep 21 14:53 data1.yml
lrwxrwxrwx  1 sestegra sestegra    9 Sep 21 14:31 data2.yml -> data1.yml

After running yq command

$ ls -l | grep data
-rw-rw-r-- 1 sestegra sestegra   50 Sep 21 14:55 data1.yml
-rw-rw-r-- 1 sestegra sestegra   67 Sep 21 14:58 data2.yml

Expected behavior

data2.yml file must be a symlink after update.

Additional context If the same command is ran with data1.yml and /tmp on different filesystem partitions, it works as expected.

The difference occurs when calling renameat. If on a different partitions, the first strace occurs.

renameat(AT_FDCWD, "/tmp/temp736164879", AT_FDCWD, "data2.yml") = -1 EXDEV (Invalid cross-device link)
renameat(AT_FDCWD, "/tmp/temp2419478472", AT_FDCWD, "data2.yml") = 0
sestegra commented 3 hours ago

Verbose log when data2.yml is on a different partition as /tmp. data2.yml is still a symlink.

❯ yq -v -i '.components += "test"' data2.yml
[DEBUG] 15:47:51 utils.go:174                      maybeFile                 checking '.components += "test"' is a file
[DEBUG] 15:47:51 utils.go:179                      maybeFile                 error: stat .components += "test": no such file or directory
[DEBUG] 15:47:51 utils.go:183                      maybeFile                 result: false
[DEBUG] 15:47:51 utils.go:236                      processArgs               processed args: [.components += "test" data2.yml]
[DEBUG] 15:47:51 utils.go:174                      maybeFile                 checking '.components += "test"' is a file
[DEBUG] 15:47:51 utils.go:179                      maybeFile                 error: stat .components += "test": no such file or directory
[DEBUG] 15:47:51 utils.go:183                      maybeFile                 result: false
[DEBUG] 15:47:51 utils.go:238                      processArgs               assuming expression is '.components += "test"'
[DEBUG] 15:47:51 format.go:112                     FormatStringFromFilename  checking filename 'data2.yml' for auto format detection
[DEBUG] 15:47:51 format.go:116                     FormatStringFromFilename  detected format 'yml'
[DEBUG] 15:47:51 utils.go:97                       initCommand               Using input format yml
[DEBUG] 15:47:51 utils.go:98                       initCommand               Using output format yml
[DEBUG] 15:47:51 write_in_place_handler.go:41      CreateTempFile            WriteInPlaceHandler: writing to tempfile: /tmp/temp3965178373
[DEBUG] 15:47:51 expression_parser.go:28           ParseExpression           Parsing expression: [.components += "test"]
[DEBUG] 15:47:51 lexer_participle.go:284           func69                    PathToken components
[DEBUG] 15:47:51 lexer_participle.go:377           func58                    rawTokenvalue: "test"
[DEBUG] 15:47:51 lexer_participle.go:379           func58                    unwrapped: test
[DEBUG] 15:47:51 lexer_participle.go:382           func58                    replaced: test
[DEBUG] 15:47:51 lexer.go:107                      handleToken               processing components (55)
[DEBUG] 15:47:51 lexer.go:144                      handleToken               adding token to the fixed list
[DEBUG] 15:47:51 lexer.go:107                      handleToken               processing ADD_ASSIGN (40)
[DEBUG] 15:47:51 lexer.go:144                      handleToken               adding token to the fixed list
[DEBUG] 15:47:51 lexer.go:107                      handleToken               processing test (string) (50)
[DEBUG] 15:47:51 lexer.go:144                      handleToken               adding token to the fixed list
[DEBUG] 15:47:51 expression_postfix.go:46          ConvertToPostfix          postfix processing currentToken components (55)
[DEBUG] 15:47:51 expression_postfix.go:122         ConvertToPostfix          put components (55) onto the opstack
[DEBUG] 15:47:51 expression_postfix.go:46          ConvertToPostfix          postfix processing currentToken ADD_ASSIGN (40)
[DEBUG] 15:47:51 expression_postfix.go:24          popOpToResult             popped components (55) from opstack to results
[DEBUG] 15:47:51 expression_postfix.go:122         ConvertToPostfix          put ADD_ASSIGN (40) onto the opstack
[DEBUG] 15:47:51 expression_postfix.go:46          ConvertToPostfix          postfix processing currentToken test (string) (50)
[DEBUG] 15:47:51 expression_postfix.go:122         ConvertToPostfix          put test (string) (50) onto the opstack
[DEBUG] 15:47:51 expression_postfix.go:46          ConvertToPostfix          postfix processing currentToken )
[DEBUG] 15:47:51 expression_postfix.go:24          popOpToResult             popped test (string) (50) from opstack to results
[DEBUG] 15:47:51 expression_postfix.go:24          popOpToResult             popped ADD_ASSIGN (40) from opstack to results
[DEBUG] 15:47:51 expression_postfix.go:126         ConvertToPostfix          opstackLen: 0
[DEBUG] 15:47:51 expression_postfix.go:137         ConvertToPostfix          PostFix Result:
[DEBUG] 15:47:51 expression_postfix.go:139         ConvertToPostfix          > components
[DEBUG] 15:47:51 expression_postfix.go:139         ConvertToPostfix          > test (string)
[DEBUG] 15:47:51 expression_postfix.go:139         ConvertToPostfix          > ADD_ASSIGN
[DEBUG] 15:47:51 expression_parser.go:50           createExpressionTree      pathTree components
[DEBUG] 15:47:51 expression_parser.go:50           createExpressionTree      pathTree test (string)
[DEBUG] 15:47:51 expression_parser.go:50           createExpressionTree      pathTree ADD_ASSIGN
[DEBUG] 15:47:51 candidate_node_yaml.go:109        UnmarshalYAML             UnmarshalYAML !!map
[DEBUG] 15:47:51 candidate_node_yaml.go:122        UnmarshalYAML             UnmarshalYAML -  a mapping node
[DEBUG] 15:47:51 candidate_node_yaml.go:109        UnmarshalYAML             UnmarshalYAML !!str
[DEBUG] 15:47:51 candidate_node_yaml.go:117        UnmarshalYAML             UnmarshalYAML -  a scalar
[DEBUG] 15:47:51 candidate_node_yaml.go:109        UnmarshalYAML             UnmarshalYAML !!str
[DEBUG] 15:47:51 candidate_node_yaml.go:117        UnmarshalYAML             UnmarshalYAML -  a scalar
[DEBUG] 15:47:51 candidate_node_yaml.go:109        UnmarshalYAML             UnmarshalYAML !!str
[DEBUG] 15:47:51 candidate_node_yaml.go:117        UnmarshalYAML             UnmarshalYAML -  a scalar
[DEBUG] 15:47:51 candidate_node_yaml.go:109        UnmarshalYAML             UnmarshalYAML !!str
[DEBUG] 15:47:51 candidate_node_yaml.go:117        UnmarshalYAML             UnmarshalYAML -  a scalar
[DEBUG] 15:47:51 candidate_node_yaml.go:145        UnmarshalYAML             UnmarshalYAML -  finished mapping node
[DEBUG] 15:47:51 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: ADD_ASSIGN
[DEBUG] 15:47:51 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::4 kids
[DEBUG] 15:47:51 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: components
[DEBUG] 15:47:51 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::4 kids
[DEBUG] 15:47:51 operator_traverse_path.go:23      traversePathOperator      traversePathOperator
[DEBUG] 15:47:51 operator_traverse_path.go:38      traverse                  Traversing D0, P, MappingNode (!!map)::4 kids
[DEBUG] 15:47:51 operator_traverse_path.go:56      traverse                  its a map with 2 entries
[DEBUG] 15:47:51 operator_traverse_path.go:230     traverseMap               no matches, creating one for D0, P, ScalarNode (!!str)::components
[DEBUG] 15:47:51 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: ASSIGN
[DEBUG] 15:47:51 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:47:51 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: REF
[DEBUG] 15:47:51 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:47:51 operator_assign.go:40             assignUpdateOperator      assignUpdateOperator prefs: {false false false}
[DEBUG] 15:47:51 operators.go:161                  crossFunctionWithPrefs    crossFunction evaluate apart!
[DEBUG] 15:47:51 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: REF
[DEBUG] 15:47:51 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:47:51 operators.go:119                  doCrossFunc               crossFunction LHS len: 1
[DEBUG] 15:47:51 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: ADD
[DEBUG] 15:47:51 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:47:51 operator_add.go:40                addOperator               Add operator
[DEBUG] 15:47:51 operators.go:161                  crossFunctionWithPrefs    crossFunction evaluate apart!
[DEBUG] 15:47:51 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: REF
[DEBUG] 15:47:51 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:47:51 operators.go:119                  doCrossFunc               crossFunction LHS len: 1
[DEBUG] 15:47:51 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: test (string)
[DEBUG] 15:47:51 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:47:51 candidate_node.go:394             UpdateAttributesFrom      UpdateAttributesFrom: n: D0, Pcomponents, ScalarNode (!!null)::test other: D0, Pcomponents, ScalarNode (!!str)::test
[DEBUG] 15:47:51 printer.go:73                     PrintResults              PrintResults for 1 matches
[DEBUG] 15:47:51 printer.go:100                    PrintResults              print sep logic: p.firstTimePrinting: false, previousDocIndex: 0
[DEBUG] 15:47:51 printer.go:101                    PrintResults              D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:47:51 encoder_yaml.go:77                Encode                    encoderYaml - going to print D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:47:51 candidate_node_yaml.go:182        MarshalYAML               MarshalYAML to yaml: !!map
[DEBUG] 15:47:51 candidate_node_yaml.go:201        MarshalYAML               original style: 0
[DEBUG] 15:47:51 candidate_node_yaml.go:202        MarshalYAML               original: D0, P, MappingNode (!!map)::6 kids, tag: !!map, style: 0, kind: false
[DEBUG] 15:47:51 candidate_node_yaml.go:182        MarshalYAML               MarshalYAML to yaml: !!str
[DEBUG] 15:47:51 candidate_node_yaml.go:190        MarshalYAML               MarshalYAML - scalar: this
[DEBUG] 15:47:51 candidate_node_yaml.go:182        MarshalYAML               MarshalYAML to yaml: !!str
[DEBUG] 15:47:51 candidate_node_yaml.go:190        MarshalYAML               MarshalYAML - scalar: should really work
[DEBUG] 15:47:51 candidate_node_yaml.go:182        MarshalYAML               MarshalYAML to yaml: !!str
[DEBUG] 15:47:51 candidate_node_yaml.go:190        MarshalYAML               MarshalYAML - scalar: but
[DEBUG] 15:47:51 candidate_node_yaml.go:182        MarshalYAML               MarshalYAML to yaml: !!str
[DEBUG] 15:47:51 candidate_node_yaml.go:190        MarshalYAML               MarshalYAML - scalar: it strangely didn't
[DEBUG] 15:47:51 candidate_node_yaml.go:182        MarshalYAML               MarshalYAML to yaml: !!str
[DEBUG] 15:47:51 candidate_node_yaml.go:190        MarshalYAML               MarshalYAML - scalar: components
[DEBUG] 15:47:51 candidate_node_yaml.go:182        MarshalYAML               MarshalYAML to yaml: !!str
[DEBUG] 15:47:51 candidate_node_yaml.go:190        MarshalYAML               MarshalYAML - scalar: test
[DEBUG] 15:47:51 printer.go:150                    PrintResults              done printing results
[DEBUG] 15:47:51 write_in_place_handler.go:47      FinishWriteInPlace        Going to write in place, evaluatedSuccessfully=true, target=data2.yml
[DEBUG] 15:47:51 write_in_place_handler.go:50      FinishWriteInPlace        Moving temp file to target
[DEBUG] 15:47:51 file_utils.go:11                  tryRenameFile             Error renaming from /tmp/temp3965178373 to data2.yml, attempting to copy contents
[DEBUG] 15:47:51 file_utils.go:12                  tryRenameFile             rename /tmp/temp3965178373 data2.yml: invalid cross-device link
[DEBUG] 15:47:51 file_utils.go:13                  tryRenameFile             going to try copying instead
[DEBUG] 15:47:51 file_utils.go:25                  tryRemoveTempFile         Removing temp file: /tmp/temp3965178373
sestegra commented 3 hours ago

Verbose log when data1.yml is on the same partition as /tmp data2.yml symlink is replaced by regular file.

[DEBUG] 15:50:26 utils.go:174                      maybeFile                 checking '.components += "test"' is a file
[DEBUG] 15:50:26 utils.go:179                      maybeFile                 error: stat .components += "test": no such file or directory
[DEBUG] 15:50:26 utils.go:183                      maybeFile                 result: false
[DEBUG] 15:50:26 utils.go:236                      processArgs               processed args: [.components += "test" data2.yml]
[DEBUG] 15:50:26 utils.go:174                      maybeFile                 checking '.components += "test"' is a file
[DEBUG] 15:50:26 utils.go:179                      maybeFile                 error: stat .components += "test": no such file or directory
[DEBUG] 15:50:26 utils.go:183                      maybeFile                 result: false
[DEBUG] 15:50:26 utils.go:238                      processArgs               assuming expression is '.components += "test"'
[DEBUG] 15:50:26 format.go:112                     FormatStringFromFilename  checking filename 'data2.yml' for auto format detection
[DEBUG] 15:50:26 format.go:116                     FormatStringFromFilename  detected format 'yml'
[DEBUG] 15:50:26 utils.go:97                       initCommand               Using input format yml
[DEBUG] 15:50:26 utils.go:98                       initCommand               Using output format yml
[DEBUG] 15:50:26 write_in_place_handler.go:41      CreateTempFile            WriteInPlaceHandler: writing to tempfile: /tmp/temp2384047187
[DEBUG] 15:50:26 expression_parser.go:28           ParseExpression           Parsing expression: [.components += "test"]
[DEBUG] 15:50:26 lexer_participle.go:284           func69                    PathToken components
[DEBUG] 15:50:26 lexer_participle.go:377           func58                    rawTokenvalue: "test"
[DEBUG] 15:50:26 lexer_participle.go:379           func58                    unwrapped: test
[DEBUG] 15:50:26 lexer_participle.go:382           func58                    replaced: test
[DEBUG] 15:50:26 lexer.go:107                      handleToken               processing components (55)
[DEBUG] 15:50:26 lexer.go:144                      handleToken               adding token to the fixed list
[DEBUG] 15:50:26 lexer.go:107                      handleToken               processing ADD_ASSIGN (40)
[DEBUG] 15:50:26 lexer.go:144                      handleToken               adding token to the fixed list
[DEBUG] 15:50:26 lexer.go:107                      handleToken               processing test (string) (50)
[DEBUG] 15:50:26 lexer.go:144                      handleToken               adding token to the fixed list
[DEBUG] 15:50:26 expression_postfix.go:46          ConvertToPostfix          postfix processing currentToken components (55)
[DEBUG] 15:50:26 expression_postfix.go:122         ConvertToPostfix          put components (55) onto the opstack
[DEBUG] 15:50:26 expression_postfix.go:46          ConvertToPostfix          postfix processing currentToken ADD_ASSIGN (40)
[DEBUG] 15:50:26 expression_postfix.go:24          popOpToResult             popped components (55) from opstack to results
[DEBUG] 15:50:26 expression_postfix.go:122         ConvertToPostfix          put ADD_ASSIGN (40) onto the opstack
[DEBUG] 15:50:26 expression_postfix.go:46          ConvertToPostfix          postfix processing currentToken test (string) (50)
[DEBUG] 15:50:26 expression_postfix.go:122         ConvertToPostfix          put test (string) (50) onto the opstack
[DEBUG] 15:50:26 expression_postfix.go:46          ConvertToPostfix          postfix processing currentToken )
[DEBUG] 15:50:26 expression_postfix.go:24          popOpToResult             popped test (string) (50) from opstack to results
[DEBUG] 15:50:26 expression_postfix.go:24          popOpToResult             popped ADD_ASSIGN (40) from opstack to results
[DEBUG] 15:50:26 expression_postfix.go:126         ConvertToPostfix          opstackLen: 0
[DEBUG] 15:50:26 expression_postfix.go:137         ConvertToPostfix          PostFix Result:
[DEBUG] 15:50:26 expression_postfix.go:139         ConvertToPostfix          > components
[DEBUG] 15:50:26 expression_postfix.go:139         ConvertToPostfix          > test (string)
[DEBUG] 15:50:26 expression_postfix.go:139         ConvertToPostfix          > ADD_ASSIGN
[DEBUG] 15:50:26 expression_parser.go:50           createExpressionTree      pathTree components
[DEBUG] 15:50:26 expression_parser.go:50           createExpressionTree      pathTree test (string)
[DEBUG] 15:50:26 expression_parser.go:50           createExpressionTree      pathTree ADD_ASSIGN
[DEBUG] 15:50:26 candidate_node_yaml.go:109        UnmarshalYAML             UnmarshalYAML !!map
[DEBUG] 15:50:26 candidate_node_yaml.go:122        UnmarshalYAML             UnmarshalYAML -  a mapping node
[DEBUG] 15:50:26 candidate_node_yaml.go:109        UnmarshalYAML             UnmarshalYAML !!str
[DEBUG] 15:50:26 candidate_node_yaml.go:117        UnmarshalYAML             UnmarshalYAML -  a scalar
[DEBUG] 15:50:26 candidate_node_yaml.go:109        UnmarshalYAML             UnmarshalYAML !!str
[DEBUG] 15:50:26 candidate_node_yaml.go:117        UnmarshalYAML             UnmarshalYAML -  a scalar
[DEBUG] 15:50:26 candidate_node_yaml.go:109        UnmarshalYAML             UnmarshalYAML !!str
[DEBUG] 15:50:26 candidate_node_yaml.go:117        UnmarshalYAML             UnmarshalYAML -  a scalar
[DEBUG] 15:50:26 candidate_node_yaml.go:109        UnmarshalYAML             UnmarshalYAML !!str
[DEBUG] 15:50:26 candidate_node_yaml.go:117        UnmarshalYAML             UnmarshalYAML -  a scalar
[DEBUG] 15:50:26 candidate_node_yaml.go:145        UnmarshalYAML             UnmarshalYAML -  finished mapping node
[DEBUG] 15:50:26 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: ADD_ASSIGN
[DEBUG] 15:50:26 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::4 kids
[DEBUG] 15:50:26 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: components
[DEBUG] 15:50:26 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::4 kids
[DEBUG] 15:50:26 operator_traverse_path.go:23      traversePathOperator      traversePathOperator
[DEBUG] 15:50:26 operator_traverse_path.go:38      traverse                  Traversing D0, P, MappingNode (!!map)::4 kids
[DEBUG] 15:50:26 operator_traverse_path.go:56      traverse                  its a map with 2 entries
[DEBUG] 15:50:26 operator_traverse_path.go:230     traverseMap               no matches, creating one for D0, P, ScalarNode (!!str)::components
[DEBUG] 15:50:26 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: ASSIGN
[DEBUG] 15:50:26 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:50:26 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: REF
[DEBUG] 15:50:26 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:50:26 operator_assign.go:40             assignUpdateOperator      assignUpdateOperator prefs: {false false false}
[DEBUG] 15:50:26 operators.go:161                  crossFunctionWithPrefs    crossFunction evaluate apart!
[DEBUG] 15:50:26 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: REF
[DEBUG] 15:50:26 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:50:26 operators.go:119                  doCrossFunc               crossFunction LHS len: 1
[DEBUG] 15:50:26 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: ADD
[DEBUG] 15:50:26 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:50:26 operator_add.go:40                addOperator               Add operator
[DEBUG] 15:50:26 operators.go:161                  crossFunctionWithPrefs    crossFunction evaluate apart!
[DEBUG] 15:50:26 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: REF
[DEBUG] 15:50:26 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:50:26 operators.go:119                  doCrossFunc               crossFunction LHS len: 1
[DEBUG] 15:50:26 data_tree_navigator.go:57         GetMatchingNodes          Processing Op: test (string)
[DEBUG] 15:50:26 data_tree_navigator.go:60         GetMatchingNodes          D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:50:26 candidate_node.go:394             UpdateAttributesFrom      UpdateAttributesFrom: n: D0, Pcomponents, ScalarNode (!!null)::test other: D0, Pcomponents, ScalarNode (!!str)::test
[DEBUG] 15:50:26 printer.go:73                     PrintResults              PrintResults for 1 matches
[DEBUG] 15:50:26 printer.go:100                    PrintResults              print sep logic: p.firstTimePrinting: false, previousDocIndex: 0
[DEBUG] 15:50:26 printer.go:101                    PrintResults              D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:50:26 encoder_yaml.go:77                Encode                    encoderYaml - going to print D0, P, MappingNode (!!map)::6 kids
[DEBUG] 15:50:26 candidate_node_yaml.go:182        MarshalYAML               MarshalYAML to yaml: !!map
[DEBUG] 15:50:26 candidate_node_yaml.go:201        MarshalYAML               original style: 0
[DEBUG] 15:50:26 candidate_node_yaml.go:202        MarshalYAML               original: D0, P, MappingNode (!!map)::6 kids, tag: !!map, style: 0, kind: false
[DEBUG] 15:50:26 candidate_node_yaml.go:182        MarshalYAML               MarshalYAML to yaml: !!str
[DEBUG] 15:50:26 candidate_node_yaml.go:190        MarshalYAML               MarshalYAML - scalar: this
[DEBUG] 15:50:26 candidate_node_yaml.go:182        MarshalYAML               MarshalYAML to yaml: !!str
[DEBUG] 15:50:26 candidate_node_yaml.go:190        MarshalYAML               MarshalYAML - scalar: should really work
[DEBUG] 15:50:26 candidate_node_yaml.go:182        MarshalYAML               MarshalYAML to yaml: !!str
[DEBUG] 15:50:26 candidate_node_yaml.go:190        MarshalYAML               MarshalYAML - scalar: but
[DEBUG] 15:50:26 candidate_node_yaml.go:182        MarshalYAML               MarshalYAML to yaml: !!str
[DEBUG] 15:50:26 candidate_node_yaml.go:190        MarshalYAML               MarshalYAML - scalar: it strangely didn't
[DEBUG] 15:50:26 candidate_node_yaml.go:182        MarshalYAML               MarshalYAML to yaml: !!str
[DEBUG] 15:50:26 candidate_node_yaml.go:190        MarshalYAML               MarshalYAML - scalar: components
[DEBUG] 15:50:26 candidate_node_yaml.go:182        MarshalYAML               MarshalYAML to yaml: !!str
[DEBUG] 15:50:26 candidate_node_yaml.go:190        MarshalYAML               MarshalYAML - scalar: test
[DEBUG] 15:50:26 printer.go:150                    PrintResults              done printing results
[DEBUG] 15:50:26 write_in_place_handler.go:47      FinishWriteInPlace        Going to write in place, evaluatedSuccessfully=true, target=data2.yml
[DEBUG] 15:50:26 write_in_place_handler.go:50      FinishWriteInPlace        Moving temp file to target
sestegra commented 2 hours ago

Don't use os.Rename on symlink, because if newpath already exists and is not a directory, os.Rename replaces it.