mirror of
https://github.com/larksuite/cli.git
synced 2026-07-23 15:01:58 +08:00
Port changes from feat/strict-mode-identity-filter_3 branch: - Add strict mode for identity filtering and configuration - Add profile management commands (add/list/remove/rename/use) - Add credential extension framework (registry, env provider) - Add VFS abstraction layer - Refactor factory default and client options - Update shortcuts to use new credential and validation patterns Change-Id: I8c104c6b147e1901d94aefcefe35a174932c742b Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
78 lines
1.9 KiB
Go
78 lines
1.9 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package validate
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/larksuite/cli/internal/vfs"
|
|
)
|
|
|
|
// AtomicWrite writes data to path atomically by creating a temp file in the
|
|
// same directory, writing and fsyncing the data, then renaming over the target.
|
|
// It replaces os.WriteFile for all config and download file writes.
|
|
//
|
|
// os.WriteFile truncates the target before writing, so a process kill (CI timeout,
|
|
// OOM, Ctrl+C) between truncate and completion leaves the file empty or partial.
|
|
// AtomicWrite avoids this: on any failure the temp file is cleaned up and the
|
|
// original file remains untouched.
|
|
func AtomicWrite(path string, data []byte, perm os.FileMode) error {
|
|
return atomicWrite(path, perm, func(tmp *os.File) error {
|
|
_, err := tmp.Write(data)
|
|
return err
|
|
})
|
|
}
|
|
|
|
// AtomicWriteFromReader atomically copies reader contents into path.
|
|
func AtomicWriteFromReader(path string, reader io.Reader, perm os.FileMode) (int64, error) {
|
|
var copied int64
|
|
err := atomicWrite(path, perm, func(tmp *os.File) error {
|
|
n, err := io.Copy(tmp, reader)
|
|
copied = n
|
|
return err
|
|
})
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return copied, nil
|
|
}
|
|
|
|
func atomicWrite(path string, perm os.FileMode, writeFn func(tmp *os.File) error) error {
|
|
dir := filepath.Dir(path)
|
|
tmp, err := vfs.CreateTemp(dir, "."+filepath.Base(path)+".*.tmp")
|
|
if err != nil {
|
|
return fmt.Errorf("create temp file: %w", err)
|
|
}
|
|
tmpName := tmp.Name()
|
|
|
|
success := false
|
|
defer func() {
|
|
if !success {
|
|
tmp.Close()
|
|
vfs.Remove(tmpName)
|
|
}
|
|
}()
|
|
|
|
if err := tmp.Chmod(perm); err != nil {
|
|
return err
|
|
}
|
|
if err := writeFn(tmp); err != nil {
|
|
return err
|
|
}
|
|
if err := tmp.Sync(); err != nil {
|
|
return err
|
|
}
|
|
if err := tmp.Close(); err != nil {
|
|
return err
|
|
}
|
|
if err := vfs.Rename(tmpName, path); err != nil {
|
|
return err
|
|
}
|
|
success = true
|
|
return nil
|
|
}
|