feat(ansible): support blockinfile newline padding

Co-authored-by: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 02:47:08 +00:00
parent a95b6ec5d8
commit 2b12b8f860
3 changed files with 37 additions and 6 deletions

View file

@ -931,6 +931,8 @@ func moduleBlockinfileWithClient(_ *Executor, client sshFileRunner, args map[str
marker := getStringArg(args, "marker", "# {mark} ANSIBLE MANAGED BLOCK")
state := getStringArg(args, "state", "present")
create := getBoolArg(args, "create", false)
prependNewline := getBoolArg(args, "prepend_newline", false)
appendNewline := getBoolArg(args, "append_newline", false)
beginMarker := replaceN(marker, "{mark}", "BEGIN", 1)
endMarker := replaceN(marker, "{mark}", "END", 1)
@ -952,16 +954,21 @@ func moduleBlockinfileWithClient(_ *Executor, client sshFileRunner, args map[str
// Remove existing block and add new one
escapedBlock := replaceAll(block, "'", "'\\''")
blockContent := beginMarker + "\n" + escapedBlock + "\n" + endMarker
if prependNewline {
blockContent = "\n" + blockContent
}
if appendNewline {
blockContent += "\n"
}
cmd := sprintf(`
sed -i '/%s/,/%s/d' %q 2>/dev/null || true
cat >> %q << 'BLOCK_EOF'
%s
%s
%s
BLOCK_EOF
`, replaceAll(beginMarker, "/", "\\/"),
replaceAll(endMarker, "/", "\\/"),
path, path, beginMarker, escapedBlock, endMarker)
path, path, blockContent)
stdout, stderr, rc, err := client.RunScript(context.Background(), cmd)
if err != nil || rc != 0 {

View file

@ -2371,6 +2371,8 @@ func (e *Executor) moduleBlockinfile(ctx context.Context, client sshExecutorClie
marker := getStringArg(args, "marker", "# {mark} ANSIBLE MANAGED BLOCK")
state := getStringArg(args, "state", "present")
create := getBoolArg(args, "create", false)
prependNewline := getBoolArg(args, "prepend_newline", false)
appendNewline := getBoolArg(args, "append_newline", false)
beginMarker := replaceN(marker, "{mark}", "BEGIN", 1)
endMarker := replaceN(marker, "{mark}", "END", 1)
@ -2392,16 +2394,21 @@ func (e *Executor) moduleBlockinfile(ctx context.Context, client sshExecutorClie
// Remove existing block and add new one
escapedBlock := replaceAll(block, "'", "'\\''")
blockContent := beginMarker + "\n" + escapedBlock + "\n" + endMarker
if prependNewline {
blockContent = "\n" + blockContent
}
if appendNewline {
blockContent += "\n"
}
cmd := sprintf(`
sed -i '/%s/,/%s/d' %q 2>/dev/null || true
cat >> %q << 'BLOCK_EOF'
%s
%s
%s
BLOCK_EOF
`, replaceAll(beginMarker, "/", "\\/"),
replaceAll(endMarker, "/", "\\/"),
path, path, beginMarker, escapedBlock, endMarker)
path, path, blockContent)
stdout, stderr, rc, err := client.RunScript(ctx, cmd)
if err != nil || rc != 0 {

View file

@ -657,6 +657,23 @@ func TestModulesFile_ModuleBlockinfile_Good_CustomMarkers(t *testing.T) {
assert.True(t, mock.hasExecutedMethod("RunScript", "# END managed by devops"))
}
func TestModulesFile_ModuleBlockinfile_Good_NewlinePadding(t *testing.T) {
e, mock := newTestExecutorWithMock("host1")
result, err := moduleBlockinfileWithClient(e, mock, map[string]any{
"path": "/etc/hosts",
"block": "10.0.0.5 db01",
"prepend_newline": true,
"append_newline": true,
})
require.NoError(t, err)
assert.True(t, result.Changed)
cmd := mock.lastCommand().Cmd
assert.Contains(t, cmd, "\n\n# BEGIN ANSIBLE MANAGED BLOCK\n10.0.0.5 db01\n# END ANSIBLE MANAGED BLOCK\n")
}
func TestModulesFile_ModuleBlockinfile_Good_RemoveBlock(t *testing.T) {
e, mock := newTestExecutorWithMock("host1")