Skip to content

File Operations

fscm provides powerful functions for managing files, directories, and permissions.

Creating and Updating Files

The file() function is the primary way to manage file contents:

import fscm

# Create a file with string content
fscm.file("/etc/myapp.conf", "setting=value\n")

# Create with specific permissions
fscm.file(
    "/etc/myapp.conf",
    "setting=value\n",
    mode="0644",
    owner="root:root"
)

# Create from bytes
fscm.file("/etc/binary.dat", b"\x00\x01\x02\x03")

# Create from another file (Path object)
from pathlib import Path
fscm.file("/etc/config", Path("local/config.template"))

Parameters

Parameter Type Description
path str/Path Destination path
content str/bytes/Path File contents
mode str Permissions (e.g., "0644")
owner str Owner (e.g., "root:root")
sudo bool Use sudo for writing
encoding str Text encoding (default: utf-8)

Idempotency

file() only writes if the content differs:

# First call: creates file
fscm.file("/etc/config", "content")  # FileAdd change

# Second call: no change needed
fscm.file("/etc/config", "content")  # No change recorded

Using Templates

For dynamic content, use Jinja2 templates:

import fscm

# Template file: templates/nginx.conf.j2
# server {
#     listen {{ port }};
#     server_name {{ domain }};
#     root {{ root_dir }};
# }

content = fscm.template(
    "templates/nginx.conf.j2",
    port=80,
    domain="example.com",
    root_dir="/var/www/html"
)

fscm.file("/etc/nginx/sites-available/mysite", content)

Template Variables

Pass any Python objects as template variables:

servers = [
    {"name": "web1", "ip": "10.0.0.1"},
    {"name": "web2", "ip": "10.0.0.2"},
]

content = fscm.template("servers.j2", servers=servers)

Template (servers.j2):

{% for server in servers %}
{{ server.name }} ansible_host={{ server.ip }}
{% endfor %}

Creating Directories

import fscm

# Create a directory
fscm.mkdir("/var/www/myapp")

# With permissions and ownership
fscm.mkdir(
    "/var/www/myapp",
    mode="0755",
    owner="www-data:www-data"
)

# Create parent directories (like mkdir -p)
fscm.mkdir("/var/www/myapp/static/images")  # Creates all parents

Changing Permissions

import fscm

# Change mode
fscm.chmod("/var/www/myapp", "0755")

# Recursive
fscm.chmod("/var/www/myapp", "0755", flags="-R")

# Make executable
fscm.make_executable("/usr/local/bin/myscript")

Changing Ownership

import fscm

# Change owner
fscm.chown("/var/www/myapp", "www-data")

# Change owner and group
fscm.chown("/var/www/myapp", "www-data:www-data")

# Recursive
fscm.chown("/var/www/myapp", "www-data:www-data", flags="-R")

Line-in-File Operations

Modify specific lines in configuration files:

import fscm

# Ensure a line exists
fscm.lineinfile(
    "/etc/ssh/sshd_config",
    line="PermitRootLogin no",
    regexp=r"^#?PermitRootLogin"
)

# Add a line if pattern not found
fscm.lineinfile(
    "/etc/hosts",
    line="10.0.0.5 myserver",
    regexp=r"myserver$"
)

# Remove a line
fscm.lineinfile(
    "/etc/hosts",
    regexp=r"^.*oldserver.*$",
    state="absent"
)

Parameters

Parameter Type Description
path str File to modify
line str Line content to ensure
regexp str Pattern to match existing line
state str "present" or "absent"
sudo bool Use sudo

Working with Paths

The p() function provides a convenient path helper:

import fscm

# Create a path helper
path = fscm.p("/var/www/myapp")

# Chain operations
path.mkdir().chmod("0755").chown("www-data")

# Access underlying Path
print(path.path)  # PosixPath('/var/www/myapp')

# Use Path methods
if path.exists():
    content = path.read_text()

With Prefix

import fscm

# All paths relative to prefix
fscm.settings.prefix = "/opt/myapp"

path = fscm.p("config/app.conf")  # /opt/myapp/config/app.conf

File Backups

fscm automatically backs up files before modification:

import fscm

# Configure backup location
fscm.settings.backup_dir = "/var/backups/fscm"

# Set size threshold (skip large files)
fscm.settings.backup_threshold = 1024 * 1024  # 1MB

# Disable backups
fscm.settings.backup_threshold = None

# Manual backup
fscm.backup_file("/etc/nginx/nginx.conf")

Backup location:

/var/backups/fscm/etc/nginx/nginx.conf.20240115_143022

Downloading Files

Download files with checksum verification:

import fscm

# Download with SHA256 verification
fscm.download_and_check_sha(
    url="https://example.com/package.tar.gz",
    dest="/tmp/package.tar.gz",
    sha256="abc123..."
)

Removing Files

import fscm

# Remove a file
fscm.run("rm /path/to/file")

# Remove directory
fscm.run("rm -rf /path/to/dir")

# Using PathHelper
path = fscm.p("/path/to/file")
path.rm()  # Remove file
path.rm("-rf")  # Remove directory recursively

Example: Complete File Setup

import fscm
from fscm import file, mkdir, chmod, chown, template

def setup_webapp():
    # Create directory structure
    mkdir("/var/www/myapp", mode="0755")
    mkdir("/var/www/myapp/static", mode="0755")
    mkdir("/var/www/myapp/logs", mode="0755")

    # Deploy application config
    file(
        "/var/www/myapp/config.py",
        template("config.py.j2", debug=False, db_host="localhost"),
        mode="0640",
        owner="www-data:www-data"
    )

    # Deploy nginx config
    file(
        "/etc/nginx/sites-available/myapp",
        template("nginx.conf.j2", domain="example.com"),
        mode="0644"
    )

    # Enable site
    fscm.run("ln -sf /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/")

    # Set ownership recursively
    chown("/var/www/myapp", "www-data:www-data", flags="-R")

    return fscm.CHANGELIST

Next Steps