Metadata-Version: 2.4
Name: gfal-cli
Version: 0.1.20
Summary: GFAL2-compatible CLI tools based on fsspec (HTTP/HTTPS and XRootD)
Author-email: Luis Antonio Obis Aparicio <luis.obis@cern.ch>
License: BSD 3-Clause License
        
        Copyright (c) 2026, Luis Antonio Obis Aparicio
        
        Redistribution and use in source and binary forms, with or without
        modification, are permitted provided that the following conditions are met:
        
        1. Redistributions of source code must retain the above copyright notice, this
           list of conditions and the following disclaimer.
        
        2. Redistributions in binary form must reproduce the above copyright notice,
           this list of conditions and the following disclaimer in the documentation
           and/or other materials provided with the distribution.
        
        3. Neither the name of the copyright holder nor the names of its
           contributors may be used to endorse or promote products derived from
           this software without specific prior written permission.
        
        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
        AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
        DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
        FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
        SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
        CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
        OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
        OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
License-File: LICENSE
Requires-Python: >=3.9
Requires-Dist: aiohttp>=3.8.0
Requires-Dist: fsspec-xrootd>=0.5.1
Requires-Dist: fsspec>=2023.1.0
Requires-Dist: requests>=2.28.0
Provides-Extra: dev
Requires-Dist: codespell>=2.2.0; extra == 'dev'
Requires-Dist: pre-commit>=3.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# gfal-cli

A Python reimplementation of the [gfal2-util](https://github.com/cern-fts/gfal2-util) command-line tools, built on [fsspec](https://filesystem-spec.readthedocs.io/). Supports local files, **HTTP/HTTPS**, and **XRootD** (`root://`).

## Installation

### From PyPI

```bash
pip install gfal-cli
```

### From GitHub Releases (Latest RPM/DEB)

We provide bundled, version-less packages for various distributions. These are installed into an isolated `/opt/gfal-cli` directory to prevent system conflicts.

**AlmaLinux 9**:
```bash
dnf install -y https://github.com/lobis/gfal-cli/releases/latest/download/python3-gfal-cli-latest-el9.rpm
```

**AlmaLinux 10**:
```bash
dnf install -y https://github.com/lobis/gfal-cli/releases/latest/download/python3-gfal-cli-latest-el10.rpm
```

**Ubuntu (DEB)**:
```bash
curl -L -O https://github.com/lobis/gfal-cli/releases/latest/download/python3-gfal-cli-latest.deb
sudo apt-get update && sudo apt-get install -y ./python3-gfal-cli-latest.deb
```

After installation the following commands are available on your `PATH`:

| Command | Description |
|---------|-------------|
| `gfal-ls` | List directory contents |
| `gfal-cp` / `gfal-copy` | Copy files |
| `gfal-rm` | Remove files or directories |
| `gfal-mkdir` | Create directories |
| `gfal-stat` | Display file status |
| `gfal-cat` | Print file contents to stdout |
| `gfal-save` | Write stdin to a remote file |
| `gfal-rename` | Rename / move a file |
| `gfal-chmod` | Change file permissions |
| `gfal-sum` | Compute file checksums |
| `gfal-xattr` | Get or set extended attributes |

## Quick start

All commands accept any URL that fsspec understands. Local paths must be given as `file://` URIs.

```bash
# Stat a local file
gfal-stat file:///tmp/test.txt

# List a remote HTTP directory
gfal-ls https://example.com/data/

# Copy a file from XRootD to local
gfal-cp root://eosuser.cern.ch//eos/user/j/jdoe/file.root file:///tmp/file.root

# Copy with checksum verification
gfal-cp -K ADLER32 root://server//path/src.dat file:///tmp/dst.dat

# Recursive copy
gfal-cp -r root://server//path/srcdir/ file:///tmp/dstdir/

# Remove a directory tree
gfal-rm -r file:///tmp/old_dir/

# Compute a checksum
gfal-sum file:///tmp/file.root ADLER32
```

## Command reference

### `gfal-ls`

```
gfal-ls [OPTIONS] URI [URI ...]
```

| Option | Description |
|--------|-------------|
| `-l` | Long listing (permissions, size, date) |
| `-a` / `--all` | Show hidden files (names starting with `.`) |
| `-d` / `--directory` | List the entry itself, not its contents |
| `-H` / `--human-readable` | Human-readable sizes with `-l` (e.g. `1.2M`) |
| `-r` / `--reverse` | Reverse sort order |
| `--time-style` | Timestamp format: `locale` (default), `iso`, `long-iso`, `full-iso` |
| `--full-time` | Equivalent to `--time-style=full-iso` |
| `--color` | Colorise output: `auto` (default), `always`, `never` |

```bash
gfal-ls -lH root://server//eos/data/
gfal-ls -la file:///tmp/mydir/
gfal-ls -l --time-style=full-iso https://example.com/files/
```

### `gfal-cp` / `gfal-copy`

```
gfal-cp [OPTIONS] SRC [SRC ...] DST
```

| Option | Description |
|--------|-------------|
| `-f` / `--force` | Overwrite destination if it exists |
| `-r` / `-R` / `--recursive` | Copy directories recursively |
| `-p` / `--parent` | Create parent directories at destination as needed |
| `-K ALG` / `--checksum ALG` | Verify checksum after copy (`ADLER32`, `MD5`, `SHA256`, …) |
| `--checksum-mode` | `both` (default), `source`, `target` |
| `--dry-run` | Show what would be copied without copying |
| `--from-file FILE` | Read source URIs from a file (one per line) |
| `--abort-on-failure` | Stop after the first failed transfer |
| `--transfer-timeout N` | Per-file timeout in seconds (0 = no timeout) |
| `--tpc` | Attempt third-party copy, fall back to streaming |
| `--tpc-only` | Require third-party copy (fail if unsupported) |

```bash
# Simple copy
gfal-cp file:///tmp/src.txt https://server/dst.txt

# Force overwrite with ADLER32 verification
gfal-cp -f -K ADLER32 root://server//path/file.root file:///tmp/file.root

# Recursive copy, create parents
gfal-cp -r -p root://server//eos/srcdir/ file:///tmp/dstdir/

# Third-party copy between two XRootD servers
gfal-cp --tpc root://src-server//path/file root://dst-server//path/file
```

### `gfal-rm`

```
gfal-rm [OPTIONS] URI [URI ...]
```

| Option | Description |
|--------|-------------|
| `-r` / `-R` / `--recursive` | Remove directories and their contents |
| `--dry-run` | Show what would be deleted without deleting |
| `--from-file FILE` | Read URIs to delete from a file |
| `--just-delete` | Skip the stat check and delete directly |

```bash
gfal-rm file:///tmp/old.txt
gfal-rm -r root://server//eos/old_dir/
gfal-rm --dry-run root://server//eos/dir/     # preview only
```

### `gfal-stat`

```
gfal-stat URI [URI ...]
```

Prints POSIX-style stat information (size, permissions, timestamps):

```
  File: 'root://server//eos/data/file.root'
  Size: 1048576        regular file
Access: (0644/-rw-r--r--)      Uid: 1000   Gid: 1000
Access: 2025-06-01 12:34:56.000000
Modify: 2025-06-01 12:34:56.000000
Change: 2025-06-01 12:34:56.000000
```

### `gfal-mkdir`

```
gfal-mkdir [OPTIONS] URI [URI ...]
```

| Option | Description |
|--------|-------------|
| `-p` / `--parents` | Create intermediate parent directories; no error if already exists |
| `-m MODE` | Permissions in octal (default: `755`) |

```bash
gfal-mkdir root://server//eos/user/j/jdoe/newdir
gfal-mkdir -p root://server//eos/user/j/jdoe/a/b/c
```

### `gfal-sum`

```
gfal-sum URI ALGORITHM
```

Supported algorithms: `ADLER32`, `CRC32`, `CRC32C`, `MD5`, `SHA1`, `SHA256`, `SHA512`.

```bash
gfal-sum file:///tmp/file.root ADLER32
# file:///tmp/file.root 0a1b2c3d
```

### `gfal-cat`

```
gfal-cat URI [URI ...]
```

Prints file contents to stdout. Multiple files are concatenated.

### `gfal-save`

```
gfal-save URI
```

Reads from stdin and writes to the given URI.

```bash
echo "hello" | gfal-save root://server//eos/user/j/jdoe/hello.txt
```

### `gfal-rename`

```
gfal-rename SOURCE DESTINATION
```

### `gfal-chmod`

```
gfal-chmod MODE URI [URI ...]
```

MODE is an octal permission string, e.g. `0644` or `755`.

## Common options

Every command accepts these global flags:

| Option | Description |
|--------|-------------|
| `-v` / `--verbose` | Enable verbose output |
| `-t N` / `--timeout N` | Global timeout in seconds |
| `-E CERT` / `--cert CERT` | Path to client certificate (PEM) |
| `--key KEY` | Path to client key (PEM) |
| `--no-verify` | Disable TLS certificate verification |
| `--log-file FILE` | Write log output to a file |

## Authentication

**X.509 proxy (XRootD / HTTPS):** If `X509_USER_PROXY` is set or a proxy exists at `/tmp/x509up_u<uid>`, it is used automatically. Override with `-E`/`--key`.

**HTTPS client certificates:** Pass `--cert` and `--key` for mutual TLS authentication.

## Development

```bash
git clone https://github.com/lobis/gfal-cli.git
cd gfal-cli

python -m venv .venv
source .venv/bin/activate

pip install -e ".[dev]"
pytest tests/
```
