fsx is a small Go package for filesystem helpers that sit just above os and path/filepath. It is designed for applications that need compact, easy-to-review helpers for expanded user paths, containment checks, file predicates, extension matching, and atomic writes.
The package is standard-library-only, has no external module dependencies, and targets the latest Go toolchain version declared in go.mod. It intentionally keeps one short package name instead of exposing broad utils packages.
- User path expansion for leading
~and$HOME - Path existence checks for files, directories, and any filesystem entry
- Directory containment checks that normalize relative traversal
- Case-insensitive file extension matching
- Atomic file replacement with permissions
- Zero third-party dependencies
- Apache-2.0 licensed
go get github.com/slashdevops/fsxUpdate to the latest available version:
go get -u github.com/slashdevops/fsx- Go 1.26.3 or newer, matching the latest Go version declared by the module
- No external Go modules or third-party dependencies
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"github.com/slashdevops/fsx"
)
func main() {
configPath := fsx.ExpandPath("~/app/config.yaml")
if !fsx.HasExtension(configPath, "yaml") {
log.Fatal("config file must be YAML")
}
if err := os.MkdirAll(filepath.Dir(configPath), 0o755); err != nil {
log.Fatal(err)
}
if err := fsx.WriteFileAtomic(configPath, []byte("enabled: true\n"), 0o600); err != nil {
log.Fatal(err)
}
fmt.Println(filepath.Base(configPath))
}func ExpandPath(path string) stringExpandPath replaces a leading ~ with the current user's home directory and replaces $HOME references with the HOME environment variable. Empty paths are returned unchanged.
configPath := fsx.ExpandPath("~/app/config.yaml")func Exists(path string) boolExists reports whether a file, directory, or other filesystem entry exists. The path is expanded before it is checked.
if fsx.Exists("~/app/config.yaml") {
// read config
}func IsDir(path string) boolIsDir reports whether the expanded path exists and is a directory.
if !fsx.IsDir("~/app") {
return errors.New("app directory does not exist")
}func IsFile(path string) boolIsFile reports whether the expanded path exists and is a regular file.
if !fsx.IsFile("~/app/config.yaml") {
return errors.New("config file does not exist")
}func IsWithin(base, target string) boolIsWithin reports whether target resolves to a path contained inside base. Both paths are expanded, cleaned, and made absolute before comparison. A target equal to base is considered within base.
This is useful before deleting or overwriting paths read from user-editable state files.
if !fsx.IsWithin(outputDir, stateFilePath) {
return fmt.Errorf("refusing to remove file outside output directory")
}func HasExtension(path string, extensions ...string) boolHasExtension reports whether path has one of the provided extensions. Matching is case-insensitive, extensions may be passed with or without a leading dot, and the path does not need to exist.
if !fsx.HasExtension("config.YAML", "yaml", "yml") {
return errors.New("config must be YAML")
}func Dir(path string) stringDir returns the directory component of path after expanding it.
dir := fsx.Dir("~/app/config.yaml")func WriteFileAtomic(path string, data []byte, perm os.FileMode) errorWriteFileAtomic writes data to a temporary file in the destination directory, sets the requested permissions, and renames the temporary file over the target path.
The destination directory must already exist. If any step fails before the rename, the temporary file is removed.
if err := fsx.WriteFileAtomic("~/app/config.yaml", data, 0o600); err != nil {
return fmt.Errorf("save config: %w", err)
}The repository follows the same GitHub quality practices used by slashdevops/e5t:
main.ymlvalidates pushes tomainwith format checks,go vet, race-enabled tests, coverage, andgo build.pr.ymlruns the same quality gate for pull requests targetingmain.codeql.ymlruns CodeQL for Go and GitHub Actions on pushes, pull requests, and a weekly schedule.release.ymlvalidates tagged releases and publishes GitHub releases with generated notes.dependabot.ymlchecks Go module and GitHub Actions updates weekly.
.
|-- .github/ GitHub Actions, CodeQL, Dependabot, and release metadata
|-- .golangci.yaml Optional local golangci-lint configuration
|-- doc.go Package documentation rendered by pkg.go.dev
|-- example_test.go Executable Go examples for documentation
|-- fsx.go Public filesystem API
|-- fsx_test.go Unit tests and benchmarks
|-- go.mod Module definition with no external requirements
|-- LICENSE Apache License 2.0
|-- README.md Project overview and usage guide
`-- SECURITY.md Vulnerability reporting policy
Run the test suite:
go test ./...Run benchmarks:
go test -bench=. ./...Check test coverage:
go test -cover ./...Run the same local quality checks used by CI:
go fix ./...
go fmt ./...
go vet ./...
go test -race -coverprofile=/tmp/fsx-coverage.txt -covermode=atomic ./...
go build ./...fsx is licensed under the Apache License 2.0.
Issues and pull requests are welcome at github.com/slashdevops/fsx. Please keep changes small, idiomatic, tested, documented, and dependency-free unless there is a clear reason to expand the project scope.