Skip to main content

Relative paths

Use context.Files.GetFile("input.txt") and context.Files.GetFolder("artifacts") for paths relative to your pipeline's working directory. These methods resolve the path immediately and retain the pipeline's file-system provider.

Pipeline.CreateBuilder chooses the pipeline working directory in this order:

  1. PipelineBuilderSettings.WorkingDirectory.
  2. PipelineBuilderSettings.ContentRootPath, then the host's configured content root (for example, DOTNET_CONTENTROOT or the --contentRoot host argument).
  3. A detected pipeline project, as described below.
  4. The process working directory when no pipeline project is found.

Pipeline project detection first checks MODULAR_PIPELINES_DIRECTORY. When set to a non-whitespace value, it must identify a directory containing both appsettings.json and a *.csproj file; an invalid value throws instead of falling back. Otherwise, detection searches the calling source file's directory and its ancestors, then AppContext.BaseDirectory and its ancestors, for those same two files. A caller's project without appsettings.json is therefore not automatically the working directory. Explicit working-directory or content-root settings bypass project detection.

Configuring a pipeline does not change Environment.CurrentDirectory.

Which directory is used?​

APIBase for a relative path
context.Files.GetFile, GetFolder, ReadAsync, WriteAsyncPipeline working directory
Context hashing, ZIP string path parameters, and downloader save pathsPipeline working directory
Command execution without a working-directory overridePipeline working directory
new FilePath("input.txt"), new FolderPath("artifacts")Process current directory at construction
Implicit string conversion to FilePath or FolderPathProcess current directory at conversion
String destinations passed to path objects' CopyTo, MoveTo, and async variantsProcess current directory at the operation

Fully qualified paths identify the same location regardless of the base directory. Path objects can be constructed outside a pipeline and do not carry its working directory. Even a file obtained from context.Files.GetFile does not make later relative string destinations pipeline-relative.

Typed path parameters retain the location resolved when the path object was created. For example, pass context.Files.GetFolder("input") as the source folder to context.Files.Zip.CreateFromDirectory. Passing a relative string to that FolderPath parameter performs the process-relative implicit conversion first; only the ZIP method's string output path is resolved against the pipeline directory.

For example, if the process runs in /runner and the pipeline working directory is /repo, new FilePath("input.txt") points to /runner/input.txt, while context.Files.GetFile("input.txt") points to /repo/input.txt.

Resolve destinations explicitly​

Inside a module, resolve both source and destination through the files context:

var source = context.Files.GetFile("input.txt");
var output = context.Files.GetFolder("artifacts");
output.Create();

var destination = context.Files.GetFile("artifacts/copy.txt");
var copied = await source.CopyToAsync(destination.Path, cancellationToken);

For a copy that keeps the file name, pass the resolved folder directly:

var copied = await source.CopyToAsync(output, cancellationToken);

Use the returned absolute Path when passing destinations to folder copy/move methods too. Avoid changing the process current directory to make relative paths match: it is shared by every concurrently running pipeline and module.

V4 migration​

Audit direct path construction, implicit string conversions, and copy/move destinations when setting WorkingDirectory or ContentRootPath. Replace pipeline-relative strings with context.Files.GetFile/GetFolder and resolved absolute destinations. This guidance documents the existing behavior; V4 does not silently rebase standalone path objects.