Reference

Understand every setting in project.json

NovaModuleTools reads project.json to decide how your project is scaffolded, built, tested, packaged, uploaded, published, and released. Use this page to understand what each setting does, what the defaults are, and when you should override them.

How to read this file

Think of project.json in layers:

  1. Top-level project settings control the build shape, source markers, resource copying, and duplicate-function validation.
  2. Manifest controls metadata for the generated PowerShell module manifest and package metadata reuse.
  3. Package controls package creation and optional raw HTTP upload defaults.
  4. Pester controls the test run configuration used by Invoke-NovaTest and Test-NovaBuild.
Default behavior matters.

You only need to add settings when you want to override the default workflow. For example, if you never add a Package section, New-NovaModulePackage still works and defaults to a single .nupkg written to artifacts/packages/.

Schema coverage.

The unified JSON schema covers all documented fields including Manifest.ReleaseNotes, Manifest.LicenseUri, Manifest.IconUri, and Pester.TestResult.OutputFormat. VS Code picks up the schema automatically when the $schema field is present in project.json.

Complete example

The packaged example project is the best real-world reference because it shows the current full configuration surface in one place.

{
  "ProjectName": "NovaExampleModule",
  "Description": "A working example project that demonstrates NovaModuleTools.",
  "Version": "0.1.0",
  "CopyResourcesToModuleRoot": false,
  "BuildRecursiveFolders": true,
  "SetSourcePath": true,
  "FailOnDuplicateFunctionNames": true,
  "Preamble": [
    "Set-StrictMode -Version Latest",
    "$ErrorActionPreference = 'Stop'"
  ],
  "Manifest": {
    "Author": "NovaModuleTools",
    "PowerShellHostVersion": "7.4",
    "GUID": "b3b4ca64-a274-4768-872d-2b3c8bc12a39",
    "Tags": ["Example", "NovaModuleTools", "PowerShell"],
    "ProjectUri": "https://www.novamoduletools.com/",
    "ReleaseNotes": "https://www.novamoduletools.com/release-notes.html",
    "LicenseUri": "https://www.novamoduletools.com/license.html"
  },
  "Package": {
    "Id": "NovaExampleModule",
    "Types": ["NuGet", "Zip"],
    "Latest": "stable",
    "OutputDirectory": {
      "Path": "artifacts/packages",
      "Clean": true
    },
    "PackageFileName": "NovaExampleModule",
    "AddVersionToFileName": true,
    "FileNamePattern": "NovaExampleModule*",
    "Authors": ["NovaModuleTools", "Example Maintainer"],
    "Description": "Example package metadata and raw upload configuration for NovaExampleModule.",
    "RepositoryUrl": "https://packages.example.test/raw/novamodule/",
    "UploadPath": "stable/latest",
    "Headers": {
      "X-Client-Id": "nova-example"
    },
    "Auth": {
      "HeaderName": "Authorization",
      "Scheme": "Bearer",
      "TokenEnvironmentVariable": "NOVA_EXAMPLE_PACKAGE_TOKEN"
    },
    "Repositories": [
      {
        "Name": "ExampleRaw",
        "Url": "https://packages.example.test/raw/novamodule/"
      }
    ]
  },
  "Pester": {
    "CodeCoverage": {
      "Enabled": false,
      "Path": [
        "src/public/*.ps1",
        "src/private/**/*.ps1",
        "src/classes/*.ps1"
      ],
      "CoveragePercentTarget": 90,
      "OutputPath": "artifacts/coverage.xml",
      "OutputFormat": "JaCoCo"
    },
    "TestResult": {
      "Enabled": true,
      "OutputFormat": "NUnitXml"
    },
    "Output": {
      "Verbosity": "Detailed"
    }
  }
}

See how to use the packaged example as a learning project

Top-level project settings

VS Code IntelliSense.

When nova init scaffolds a new project, it injects a $schema field pointing to the versioned public schema at https://www.novamoduletools.com/schema/v{major}/project.json, and also writes a .vscode/settings.json entry that maps project.json to the same URL. Together these two outputs let VS Code load the schema and provide IntelliSense, field validation, and hover descriptions without a manual trust prompt. To enable this on an existing project, add the $schema field as the first key in project.json and add the matching json.schemas entry to .vscode/settings.json.

Setting Purpose Default Workflow effect
$schema Optional URI pointing to the versioned JSON schema for this file. Injected automatically by nova init Enables VS Code IntelliSense, field validation, and hover descriptions while editing project.json.
ProjectName Name of the module and the output folder under dist/. Required Determines manifest name, module name, and many generated paths.
Description Human-readable project description. Required Feeds manifest and package metadata when you do not override them later.
Version Current semantic version stored for the project. Required Used in package names, manifest metadata, % nova version, and release flows.
CopyResourcesToModuleRoot Controls where resources are copied in the built module. false When false, resources stay in a resources/ folder. When true, they are copied into the module root.
BuildRecursiveFolders Controls recursive discovery for classes, private helpers, and tests. true Affects how nested source files and nested tests are discovered.
SetSourcePath Controls # Source: markers in the generated .psm1. true Makes parser and runtime errors easier to map back to source files.
FailOnDuplicateFunctionNames Fails the build when duplicate top-level function names are emitted. true Protects the generated module from ambiguous exports and hidden overwrites.
Preamble Optional lines written at the top of the generated .psm1. Empty array Useful for Set-StrictMode, $ErrorActionPreference, or other module-wide setup lines.

Examples

{
  "CopyResourcesToModuleRoot": true,
  "BuildRecursiveFolders": false,
  "SetSourcePath": true,
  "FailOnDuplicateFunctionNames": true,
  "Preamble": [
    "Set-StrictMode -Version Latest",
    "$ErrorActionPreference = 'Stop'"
  ]
}

Use this pattern when you want strict runtime defaults, top-level-only test discovery, and resources copied directly to the module root.

Manifest settings

These settings feed the generated PowerShell module manifest and, in several cases, package metadata.

Setting Required What it does Notes
Author Yes Sets the manifest author and the default package author when Package.Authors is omitted. Use a single author string; package logic can later expand into arrays.
PowerShellHostVersion Yes Defines the minimum PowerShell version expected by the generated module. Users see this in the manifest and module compatibility expectations.
GUID Yes Sets the manifest GUID. Generate a new GUID for each distinct module.
Tags No Adds manifest tags and is reused by package metadata when present. Optional in practice and safe to omit.
ProjectUri No Publishes a project URL in the manifest and package metadata. Explicitly described in the current build schema.
ReleaseNotes No Publishes a release notes link for packaging and user-facing metadata. Used by the current product and examples even though the build schema does not enumerate it separately yet.
LicenseUri No Publishes a license URL for packaging and manifest metadata. Used in practice and shown in the packaged example.
IconUri No Publishes an icon URL in the manifest. Used by the repository project itself and worth keeping stable when you publish publicly.

If Tags, ProjectUri, ReleaseNotes, or LicenseUri are omitted, packaging still succeeds. The corresponding package metadata fields are simply left out.

Package settings

The Package section controls two related but different concerns:

  • package creation via New-NovaModulePackage / % nova package
  • raw HTTP artifact upload via Deploy-NovaPackage / % nova deploy
Keep these concepts separate.

Package.Types, Package.Latest, and OutputDirectory decide what gets created. RepositoryUrl, Headers, Auth, and Repositories decide how existing artifacts are uploaded later.

Package creation settings

Setting Default What it controls Gotchas
Id ProjectName The package identifier and default naming base. Override this only when the package identity should differ from the module name.
Types ["NuGet"] Which package formats to create. Supported values are NuGet, Zip, .nupkg, and .zip, and matching is case-insensitive.
Latest "never" Controls whether Nova creates companion latest artifacts for each selected type. Use "stable" to update latest only for stable versions, "always" for both stable and preview versions, or "never" to disable the floating alias.
OutputDirectory.Path artifacts/packages Where packages are written. This is also the default discovery location for Deploy-NovaPackage.
OutputDirectory.Clean true Whether to clear the output directory before packaging. Set it to false only when you intentionally want to retain older files.
PackageFileName <Id>.<Version>.nupkg Base file name used for package generation. Nova normalizes the extension per package type. When you combine a custom base name with AddVersionToFileName, Nova appends the project version before the extension and can still swap that suffix for latest when needed.
AddVersionToFileName false Appends .<Version> from the top-level project version to the configured PackageFileName. Useful when PackageFileName is a stable base name such as AgentInstaller. With Latest enabled, Nova substitutes the appended version suffix with .latest for the companion artifact.
FileNamePattern <Id>* in practice Pattern used to discover matching package files for upload. When you omit it, Nova falls back to <Id>* and adds the active package type extension during discovery. When you set it, your pattern overrides that default. If PackageFileName uses a different base name than Id, update FileNamePattern too so deploy discovery still matches the generated files. A pattern that already ends with .zip or .nupkg is treated as authoritative, so MyModule.*.zip matches MyModule.1.2.3.zip and MyModule.latest.zip without discovering .nupkg files.
Authors Derived from Manifest.Author Package author metadata. Can be a single string or an array of strings.
Description Derived from top-level Description Package description metadata. Use this when the package description should differ from the module description.

Raw upload settings

Setting What it does Notes
RepositoryUrl Canonical package-level raw upload base URL. Use this when you only need one default upload target.
RawRepositoryUrl Legacy compatibility alias. If present, it still maps into RepositoryUrl when the canonical setting is missing.
UploadPath Optional extra path segment appended before the file name during upload. Useful for folder-style raw repositories such as stable/latest or preview.
Headers Generic additional HTTP headers. Merged with repository-specific headers and dynamic parameter headers when you invoke upload.
Auth.HeaderName Name of the authentication header to emit. Examples: Authorization, X-Api-Key.
Auth.Scheme Optional scheme prefix for the auth header value. Examples: Bearer, Basic.
Auth.Token Literal token value. Good for local testing only. Prefer environment variables for real secrets.
Auth.TokenEnvironmentVariable Name of the environment variable that stores the token value. This should be the variable name, not the secret itself.
Repositories Named upload targets with optional overrides. Each repository needs Name and Url, then can add UploadPath, Headers, and Auth.

Nova resolves raw-upload settings in a fixed order so CI/CD overrides stay predictable. Command arguments such as -Url / --url and -UploadPath / --upload-path win first, then the selected Package.Repositories[] entry, then package-level defaults such as Package.RepositoryUrl, Package.RawRepositoryUrl, and Package.UploadPath.

Secrets follow a matching precedence model. An explicit token value wins first, then an explicit token-environment-variable name, then the merged repository/package Auth.TokenEnvironmentVariable, and finally the merged literal Auth.Token. Prefer environment-variable indirection for real credentials.

Authentication examples

{
  "Package": {
    "RepositoryUrl": "https://packages.example/raw/",
    "Auth": {
      "HeaderName": "Authorization",
      "Scheme": "Bearer",
      "TokenEnvironmentVariable": "NOVA_PACKAGE_TOKEN"
    }
  }
}

Use this for bearer-token-style endpoints.

{
  "Package": {
    "Repositories": [
      {
        "Name": "RawRepo",
        "Url": "https://packages.example/raw/",
        "Auth": {
          "HeaderName": "X-Api-Key",
          "TokenEnvironmentVariable": "NOVA_PACKAGE_API_KEY"
        }
      }
    ]
  }
}

Use this for custom API-key headers.

{
  "Package": {
    "Repositories": [
      {
        "Name": "LocalNexus",
        "Url": "http://localhost:8081/repository/raw/modules/",
        "Auth": {
          "HeaderName": "Authorization",
          "Scheme": "Basic",
          "TokenEnvironmentVariable": "NOVA_NEXUS_BASIC_AUTH"
        }
      }
    ]
  }
}

Use this generic model for Basic auth when the endpoint expects a pre-encoded token value. In that case, the environment variable should hold the Base64 value for username:password, not the raw username or password.

Pester settings

NovaModuleTools passes your Pester settings into the managed test run configuration used by Invoke-NovaTest and Test-NovaBuild.

Setting Current usage Default/Example Where you see it
Pester.TestResult.Enabled Controls whether test results are configured for output. true in the project template and packaged example Influences the generated test result artifact behavior.
Pester.TestResult.OutputFormat Passed through by the current project template and examples. NUnitXml Determines the result format written to the managed NUnit result artifacts.
Pester.CodeCoverage.Enabled Turns the standard Nova coverage pass on or off. false in the project template and packaged example When enabled, the test workflow also writes the configured coverage report and enforces the configured target.
Pester.CodeCoverage.Path Declares which source files the coverage run analyzes. src/public/*.ps1
src/private/*.ps1
src/private/*/*.ps1
src/private/*/*/*.ps1
src/classes/*.ps1
These defaults ship in new starter projects so coverage can be enabled without hand-authoring the file list, including nested helper folders such as src/private/build/manifest/ and src/private/quality/duplicates/.
Pester.CodeCoverage.CoveragePercentTarget Sets the minimum coverage percentage the test workflow must meet. 90 in the project template and packaged example Shown in Pester's coverage summary and enforced by Nova when coverage is enabled.
Pester.CodeCoverage.OutputPath Sets where the coverage report is written. artifacts/coverage.xml The JaCoCo report file emitted when coverage is enabled.
Pester.CodeCoverage.OutputFormat Controls the coverage report format. JaCoCo Keeps the coverage artifact ready for tools that expect JaCoCo XML.
Pester.Output.Verbosity Controls Pester console verbosity. Detailed in the current project template Affects what you see during Invoke-NovaTest, Test-NovaBuild, and % nova test.

Starter projects now include the full Pester.CodeCoverage block with the explicit default source globs shown above. The minimal scaffold keeps Enabled=false, while the packaged example scaffold sets Enabled=true so coverage works against src/**/*.ps1 immediately. At runtime, Invoke-NovaTest and Test-NovaBuild both let you override verbosity and render mode temporarily with -OutputVerbosity and -OutputRenderMode.

When you enable coverage, Nova writes JaCoCo output to artifacts/coverage.xml and fails the test workflow if the measured percentage is lower than Pester.CodeCoverage.CoveragePercentTarget.

Common recipes

Minimal module project

{
  "ProjectName": "MyModule",
  "Description": "My module",
  "Version": "0.1.0",
  "Manifest": {
    "Author": "You",
    "PowerShellHostVersion": "7.4",
    "GUID": "00000000-0000-0000-0000-000000000000"
  }
}

Use this when you want the smallest hand-authored Nova project. Scaffolded projects start with additional Pester defaults; the minimal scaffold keeps CodeCoverage disabled until you opt in, while the packaged example scaffold enables it by default. Nova can still resolve its normal defaults when those sections are omitted.

Create both NuGet and Zip artifacts, plus latest aliases

{
  "Package": {
    "Types": ["NuGet", "Zip"],
    "Latest": "stable",
    "OutputDirectory": {
      "Path": "artifacts/packages",
      "Clean": true
    }
  }
}

This creates four files for stable versions when both package types are selected: versioned and latest variants for each format.

Use named raw repositories with shared defaults

{
  "Package": {
    "RepositoryUrl": "https://packages.example/raw/default/",
    "Headers": {
      "X-Client": "my-module"
    },
    "Auth": {
      "HeaderName": "Authorization",
      "Scheme": "Bearer",
      "TokenEnvironmentVariable": "NOVA_PACKAGE_TOKEN"
    },
    "Repositories": [
      {
        "Name": "Stable",
        "Url": "https://packages.example/raw/stable/"
      },
      {
        "Name": "Preview",
        "Url": "https://packages.example/raw/preview/",
        "UploadPath": "nightly"
      }
    ]
  }
}

Use package-level values for the shared default behavior, then override only the repository-specific parts that need to change. Repository-specific Headers, Auth, and UploadPath override package defaults only for the selected named target.