feat(ansible): support hard file links
Some checks are pending
CI / test (push) Waiting to run
CI / auto-fix (push) Waiting to run
CI / auto-merge (push) Waiting to run

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-03 14:15:47 +00:00
parent cd0d258768
commit bae7fa0a39
3 changed files with 48 additions and 0 deletions

View file

@ -842,6 +842,17 @@ func moduleFileWithClient(_ *Executor, client sshFileRunner, args map[string]any
return &TaskResult{Failed: true, Msg: stderr, RC: rc}, nil
}
case "hard":
src := getStringArg(args, "src", "")
if src == "" {
return nil, mockError("moduleFileWithClient", "file: src required for hard state")
}
cmd := sprintf("ln -f %q %q", src, path)
_, stderr, rc, err := client.Run(context.Background(), cmd)
if err != nil || rc != 0 {
return &TaskResult{Failed: true, Msg: stderr, RC: rc}, nil
}
case "file":
// Ensure file exists and set permissions
if mode := getStringArg(args, "mode", ""); mode != "" {

View file

@ -783,6 +783,17 @@ func (e *Executor) moduleFile(ctx context.Context, client sshExecutorClient, arg
return &TaskResult{Failed: true, Msg: stderr, RC: rc}, nil
}
case "hard":
src := getStringArg(args, "src", "")
if src == "" {
return nil, coreerr.E("Executor.moduleFile", "src required for hard state", nil)
}
cmd := sprintf("ln -f %q %q", src, path)
_, stderr, rc, err := client.Run(ctx, cmd)
if err != nil || rc != 0 {
return &TaskResult{Failed: true, Msg: stderr, RC: rc}, nil
}
case "file":
// Ensure file exists and set permissions
if mode := getStringArg(args, "mode", ""); mode != "" {

View file

@ -419,6 +419,32 @@ func TestModulesFile_ModuleFile_Good_StateLink(t *testing.T) {
assert.True(t, mock.hasExecuted(`ln -sf "/opt/node/bin/node" "/usr/local/bin/node"`))
}
func TestModulesFile_ModuleFile_Good_StateHard(t *testing.T) {
e, mock := newTestExecutorWithMock("host1")
result, err := moduleFileWithClient(e, mock, map[string]any{
"path": "/usr/local/bin/node",
"state": "hard",
"src": "/opt/node/bin/node",
})
require.NoError(t, err)
assert.True(t, result.Changed)
assert.True(t, mock.hasExecuted(`ln -f "/opt/node/bin/node" "/usr/local/bin/node"`))
}
func TestModulesFile_ModuleFile_Bad_StateHardMissingSrc(t *testing.T) {
e, mock := newTestExecutorWithMock("host1")
_, err := moduleFileWithClient(e, mock, map[string]any{
"path": "/usr/local/bin/node",
"state": "hard",
})
assert.Error(t, err)
assert.Contains(t, err.Error(), "src required for hard state")
}
func TestModulesFile_ModuleFile_Bad_LinkMissingSrc(t *testing.T) {
e, mock := newTestExecutorWithMock("host1")