DotSlash

repository·main·Indexed 21 days ago

https://github.com/facebook/dotslash

A command-line tool for simplified executable deployment that replaces platform-specific binaries with lightweight text files. It fetches, caches, and verifies binaries on-demand, allowing developers to vendor toolchains into source control efficiently. Available as a CLI, an npm package (fb-dotslash), a Python library, and a Dev Container Feature.

Tokens
16.5K
Snippets
59
Records
84
Agent score
72%

What's inside DotSlash

  1. What is DotSlash

    main

    DotSlash (dotslash) is a command-line tool designed to fetch, verify, and run executables. It maintains a local cache of fetched executables to ensure that subsequent invocations are fast.

    It is primarily used to keep heavyweight binaries out of version control repositories while ensuring developers have access to consistent, platform-specific tools for hermetic builds.

  2. What is DotSlash and how does it work?

    main

    DotSlash (dotslash) is a command-line tool designed for simplified executable deployment. It allows you to replace platform-specific, heavyweight binaries and complex shell scripts with a single, lightweight, human-readable text file (typically JSON or JSONC).

    Instead of storing large binaries directly in source control, you define the metadata for various platforms in a DotSlash file. When the file is executed, DotSlash automatically fetches, decompresses, and verifies the correct binary for the host environment. This approach facilitates reproducible builds by allowing toolchains to be checked into a repository without significantly increasing its size.

  3. How DotSlash execution works

    main

    When you invoke a DotSlash file (e.g., ./scripts/node --version), DotSlash acts as a transparent wrapper that manages the lifecycle of the underlying executable. On macOS and Linux, the process follows these steps:

    1. Shebang Expansion: The command is expanded to invoke the dotslash binary with the target file as an argument.
    2. Parsing: DotSlash parses the target file to determine the required exec invocation.
    3. Cache Check: It attempts to exec the artifact from the $DOTSLASH_CACHE.
    4. Acquisition (if missing): If the artifact is not in the cache (ENOENT), DotSlash acquires a file lock for that specific artifact to prevent race conditions.
    5. Fetch & Verify: It uses providers defined within the DotSlash file to fetch the artifact, then verifies its size and hash.
    6. Installation: The artifact is decompressed into a temporary directory, sanitized, and then moved (mv) to its final destination in the cache.
    7. Execution: Once the artifact is ready, the lock is released, and DotSlash performs a final exec, replacing the dotslash process with the actual target executable.
    ./scripts/node --version
  4. Configure DotSlash regeneration policies

    main

    DotSlash files can be managed via policies that determine when they should be automatically regenerated. Common criteria for regeneration include:

    • Periodic rebuilds: Based on a defined schedule (e.g., cron).
    • File changes: Triggered when files within a specific set of folders are modified.
    • Manual trigger: Initiated by a user.

    When a policy is satisfied, a script generates build jobs. Each job produces a compressed artifact (using zstd) which is uploaded to a blobstore. The resulting DotSlash file aggregates the metadata (URL, BLAKE3 hash, and file size) for all platforms.

  5. Configure the `path` parameter

    main

    The path identifies the file to execute within the unpacked artifact's directory.

    Strict Requirements:

    • Must be a normalized, relative UNIX path.
    • No backslashes (\) are allowed, even on Windows.
    • No absolute paths (e.g., /usr/bin/foo).
    • No current/parent directory components (e.g., ./foo or ../foo).
    • No trailing slashes (e.g., foo/).

    Behavior based on format:

    • Archive: The path is the relative path within the unpacked directory.
    • Single file: The path is the relative path where the file is written inside a new, empty directory in the cache.
  6. Understand the purpose and benefits of DotSlash

    main

    DotSlash is an ad-hoc distribution mechanism for tools, designed to allow executables to be updated atomically with code. Instead of distributing large binaries directly, DotSlash uses small (kilobyte-sized) files that lazily fetch the actual artifacts (which can be megabytes or gigabytes) only when executed.

    Key Benefits:

    • Atomic Updates: Tools can be versioned and updated alongside the source code they support.
    • Lazy Fetching: Reduces bandwidth and storage by only downloading the specific executable required for the current environment/platform when it is actually run.
    • Self-Service Tooling: Developers can introduce new tools to a project or remote execution environment simply by committing a DotSlash file, avoiding the need to rebuild custom Docker containers or environment images.
  7. DotSlash is for executables, not general file distribution

    main

    DotSlash is designed exclusively for fetching and running executable files. It is not intended as a general-purpose file distribution mechanism.

    If you need to distribute data via DotSlash, the recommended pattern is to create an executable that, when run, writes the specific data payload to a designated output folder.

  8. DotSlash File Schema and Structure

    main

    A DotSlash file is a specialized configuration file used to define executable deployment across multiple platforms.

    Requirements

    • Shebang Header: The file must start with #!/usr/bin/env dotslash followed immediately by a newline (\n or \r\n).
    • JSON Payload: The header must be followed by a JSON object. The parser is lenient and supports trailing commas as well as // and /* */ style comments.

    Top-Level Schema

    The root JSON object must contain:

    • name (string): The name of the executable.
    • platforms (map): A map where keys are platform identifiers and values are platform entries.

    Supported Platform Keys

    Platform keys follow a format inspired by Clang triples. Supported keys are:

    • linux-aarch64
    • linux-x86_64
    • macos-aarch64
    • macos-x86_64
    • windows-aarch64
    • windows-x86_64

    When running a DotSlash file, it only executes the entry corresponding to the current target platform.

    #!/usr/bin/env dotslash
    
    {
      "name": "hermes",
      "platforms": {
        "linux-x86_64": {
          "size": 47099598,
          "hash": "blake3",
          "digest": "8d2c1bcefc2ce6e278167495810c2437e8050780ebb4da567811f1d754ad198c",
          "format": "tar.gz",
          "path": "hermes",
          "providers": [
            {
              "url": "https://example.com/hermes.tar.gz"
            }
          ]
        }
      }
    }
  9. Consider Debug Symbols and Version Skew

    main

    When using DotSlash, be aware of two technical trade-offs:

    • Debug Symbols: While stripping executables reduces size, it makes debugging difficult. Consider the needs of your target users before deciding whether to deploy stripped binaries.
    • Version Skew in Monorepos: If you vendor a tool (like a compiler) as a DotSlash file in a monorepo, you may encounter version skew. Because DotSlash artifacts are fetched and cached, the vendored tool might become out of sync with the library code being developed in tandem in the same repository. This can lead to incompatible states if a single commit requires updates to both the tool and the library.
  10. Configure DotSlash Providers

    main

    A provider is a JSON object in the providers list that tells DotSlash how to fetch an artifact. DotSlash tries providers in the order they are listed until one succeeds. If you want to randomize the order (e.g., to use mirrors), add providers_order: "weighted-random" to the artifact entry. You can use a weight (integer $\ge 1$, defaults to $1$) to bias the selection.

    Currently supported providers:

    • HTTP Provider: Uses curl to fetch via a URL. Requires the url field.
    • GitHub Release Provider: Uses the GitHub CLI (gh) to fetch from a repository. Requires type, repo, tag, and name. This is useful for private repositories if gh is authenticated.
    • S3 Provider: Uses the aws CLI to fetch from S3 or compatible stores. Requires type, repo (the bucket), key, and optionally region.
    {
      "format": "tar.gz",
      "path": "hermes",
      "providers": [
        {"url": "https://primary.example.com/hermes.tar.gz", "weight": 3},
        {"url": "https://mirror1.example.com/hermes.tar.gz", "weight": 1},
        {"url": "https://mirror2.example.com/hermes.tar.gz", "weight": 1},
        {"url": "https://mirror3.example.com/hermes.tar.gz", "weight": 1}
      ],
      "providers_order": "weighted-random"
    }
  11. Understand the differences between DotSlash and package managers

    main

    DotSlash is not a full-featured package manager like RPM or APT. When deciding if DotSlash is right for your project, consider these functional differences:

    • No Dependency Management: DotSlash does not have a concept of "dependent packages." If an executable requires another tool (e.g., ESLint requiring Node.js), DotSlash will not automatically download the dependency or add it to the user's $PATH. The dependency is only fetched when the specific DotSlash file for that dependency is executed.
    • No Arbitrary File Placement: Unlike RPMs, DotSlash cannot write files to specific system directories (like /etc/bash_completion.d or /usr/share/man) during installation.

    Workaround for completions: If your tool requires shell completions, you should implement a subcommand within your CLI (similar to rustup completions) that allows users to generate these files manually.

  12. Use the DotSlash Windows Shim to run DotSlash files on Windows

    main

    Because Windows does not support shebangs and relies on file extensions for executability, you can use the DotSlash Windows Shim to run DotSlash files.

    To use it, place the shim executable in the same directory as your DotSlash file, renaming the shim to match the DotSlash file's name plus the .exe extension. When the .exe is executed, it automatically invokes dotslash with the sibling DotSlash file and forwards all arguments and I/O streams.

    Example: If your DotSlash file is named node, copy the shim executable to that directory and rename it to node.exe. Running node.exe will execute dotslash node with your provided arguments.

    # Example setup
    # If you have a file named 'node' (the DotSlash file)
    # Copy the shim to the same directory and rename it to 'node.exe'