> For the complete documentation index, see [llms.txt](https://docs.flxbl.io/flxbl/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flxbl.io/flxbl/sfp/releasing-artifacts/generating-a-release-definition.md).

# Generating a release definition

A [release definition](/flxbl/sfp/releasing-artifacts/release-definitions.md) lists the exact artifact versions that make up a release. Writing one by hand means looking up the current version of every package in the domain, so sfp generates it instead: `sfp releasecandidate generate` reads a [release config](/flxbl/sfp/development/defining-a-domain/release-config.md) at a given git reference, resolves the version of every package that config selects, and publishes the result to sfp server as a **release candidate**.

The candidate is what `sfp release` deploys. Generation sits between publishing artifacts and releasing them:

```mermaid
graph LR
    A[sfp build] --> B[sfp publish]
    B --> C[sfp releasecandidate generate]
    C --> D[sfp release --releasecandidate]
```

## Before you generate

Three things must already be in place:

* **A release config** defining the domain — which packages belong to it, and the release definition properties to apply. See [Release Config](/flxbl/sfp/development/defining-a-domain/release-config.md).
* **Published artifacts.** Generation resolves versions from git tags and then checks that each artifact actually exists in a registry the server can reach — its internal registry, or an external npm registry via a configured integration. Packages that were never built and published are left out. See [How versions and artifacts are resolved](#how-versions-and-artifacts-are-resolved).
* **A reachable sfp server**, authenticated with `--email` or `--application-token`, since the candidate is published to it.

## Generating a candidate

```bash
sfp releasecandidate generate \
  --gitref HEAD \
  --configfile config/release-config.yaml \
  --releasename "Release-2.0.0" \
  --branch main \
  --repository myorg/myrepo
```

This prints the generated definition and publishes it to the server:

```yaml
release: Release-2.0.0
releaseConfigName: core
skipIfAlreadyInstalled: true
skipArtifactUpdate: false
artifacts:
  apex-logger: 1.0.20-89
  feature-management: 1.0.19-6844956242
promotePackagesBeforeDeploymentToOrg: prod
changelog:
  workItemFilters:
    - (FGK|FFK)-[0-9]{3,4}
  workItemUrl: https://flxbl.atlassian.net/browse
  limit: 30
```

Every attribute in that file is explained in [Release Definitions](/flxbl/sfp/releasing-artifacts/release-definitions.md). What generation decides for you:

* **Which packages appear.** The project's packages, filtered through the release config's `includeOnlyArtifacts` / `excludeArtifacts`.
* **Which version each gets.** The version from the package's latest git tag at `--gitref`, confirmed to exist in a registry before the package is included. A package with no git tag, or whose artifact is missing from every registry that is checked, is left out — see [How versions and artifacts are resolved](#how-versions-and-artifacts-are-resolved).
* **Package dependencies.** Collected from the project's external package aliases, unless the release config excludes them, and merged with any explicit `packageDependencies`.
* **Promotion target.** `promotePackagesBeforeDeploymentToOrg` from the release config, or the first active release environment registered for the repository when the config does not set it.

## How versions and artifacts are resolved

Generation resolves each package's version from its **latest git tag at `--gitref`** — never from a registry — and then confirms the artifact for that version exists before including it. A package with no tag is left out before any registry is consulted.

Existence is checked in two registries, in order:

1. **sfp server's internal registry**, under `--scope` (`flxbl-io` unless overridden).
2. **The repository's external npm registry** — checked only as a fallback, and only when an [npm registry integration](/flxbl/sfp/collaborate/integrations.md) is configured on the server for the repository. The scope, registry URL, and credentials come from that integration; the scope defaults to the repository owner unless the integration sets one.

A package is included if **either** registry has the artifact at the tagged version.

### Migrating an existing project onto sfp server

The external-registry fallback is what lets you generate release candidates for a project whose earlier artifacts were published to an external npm registry, before it moved onto sfp server. The internal registry starts empty, but generation still resolves those historical versions from the external registry — **you do not have to republish the artifacts to sfp server first.** For this to work:

* **The git tags must still be in the repository.** The version comes from the tag, so a package whose tag is missing at `--gitref` is left out regardless of what any registry holds.
* **The npm integration must be configured for the repository.** Without it, only the internal registry is checked and every pre-migration package is reported missing. The fallback is best-effort: if the integration is absent or unreachable, generation logs that it is validating against the internal registry only and carries on — which then surfaces as a "no package resolved to an artifact" failure rather than an explicit configuration error.
* **The integration's scope must match where the artifacts were published.** Validation looks for `@<scope>/<package>_sfpowerscripts_artifact` at the tagged version; if the configured scope differs from the one the artifacts were originally published under, they are not found.

Validation only confirms the artifact exists — it does not copy it into sfp server's internal registry. The artifact is fetched from the external registry at release time, so keep the integration configured for as long as any candidate still references those versions.

## Releasing the candidate

The release config's `releaseName` becomes the candidate's **domain**, and `--releasename` becomes its release name. Together they address it as `domain:releaseName` — a config with `releaseName: core` generating `Release-2.0.0` produces `core:Release-2.0.0`.

`sfp release` takes that identifier and fetches the definition from the server, so no file changes hands:

```bash
sfp release --releasecandidate core:Release-2.0.0 --targetorg production --repository myorg/myrepo
```

See [Releasing to an environment](/flxbl/sfp/releasing-artifacts/releasing-to-an-environment.md) for what happens during a release.

The same identifier addresses the candidate throughout its life:

```bash
sfp releasecandidate status   -n core:Release-2.0.0 --repository myorg/myrepo
sfp releasecandidate update   -n core:Release-2.0.0 --repository myorg/myrepo --exclude pkg-a
sfp releasecandidate finalize -n core:Release-2.0.0 --repository myorg/myrepo
```

Candidates are created as `pending`. A successful deployment moves one to `in_progress`, and when the project has `autoReleaseFinalization` enabled it is finalized automatically once it has reached every release environment for its domain — otherwise `finalize` marks it fully deployed. `abort` marks a candidate undeployable. Both `finalized` and `aborted` are terminal: a candidate in either state can no longer be updated. The full command set is in the [CLI reference](/flxbl/sfp/cli-reference-v51-release-v3/advanced/releasecandidate.md).

## Command attributes

| Flag                      | Description                                                                                                                                            | Required             |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------- |
| `-c, --gitref`            | Git reference (commit SHA, tag, or branch) the artifact versions are resolved from                                                                     | Yes                  |
| `-f, --configfile`        | Path to the release config YAML file                                                                                                                   | Yes                  |
| `-n, --releasename`       | Name of the release candidate                                                                                                                          | Yes                  |
| `--repository`            | Repository identifier — `owner/repo` for GitHub and GitLab, `org/project/repo` for Azure DevOps. Defaults to `GITHUB_REPOSITORY` / `GITLAB_REPOSITORY` | Yes, when publishing |
| `-b, --branch`            | Branch recorded against the release candidate                                                                                                          | No                   |
| `-m, --metadata`          | Additional metadata in JSON format, copied into the release definition                                                                                 | No                   |
| `-s, --status`            | Initial status. Candidates are currently always created as `pending`; use `sfp releasecandidate status --set` to change it                             | No                   |
| `--scope`                 | Registry scope used when validating artifacts. Defaults to `flxbl-io`                                                                                  | No                   |
| `--include-package`       | Restrict the candidate to this package. Repeat for multiple packages. Available from v52.5.0                                                           | No                   |
| `--nopublish`             | Generate the release definition without publishing it to the server                                                                                    | No                   |
| `--output`                | Path to write the generated YAML. Requires `--nopublish`                                                                                               | No                   |
| `-e, --email`             | Email of the authenticated user. Ignored when `--application-token` is supplied                                                                        | No                   |
| `-t, --application-token` | Application token for CI/CD authentication. Can be set via `SFP_SERVER_TOKEN`                                                                          | No                   |
| `--sfp-server-url`        | URL of sfp server. Can be set via `SFP_SERVER_URL` or `sfp config:set server-url`                                                                      | No                   |

## Releasing a subset of the domain

`--include-package` narrows the candidate to named packages, rather than everything the release config selects:

```bash
sfp releasecandidate generate \
  --gitref HEAD \
  --configfile config/release-config.yaml \
  --releasename "Hotfix-2.0.1" \
  --repository myorg/myrepo \
  --include-package src-sales-core \
  --include-package src-lead-scoring
```

Selection is exact: if a named package does not resolve to an artifact, the command fails rather than generating a candidate that quietly omits it.

## Writing the definition to a file

`--nopublish` skips the server, and `--output` writes the YAML to disk — useful for inspecting what a config would produce, or for feeding [`sfp repo:patch`](/flxbl/sfp/releasing-artifacts/patching-releases.md):

```bash
mkdir -p releases

sfp releasecandidate generate \
  --gitref HEAD \
  --configfile config/release-config.yaml \
  --releasename "Release-2.0.0" \
  --repository myorg/myrepo \
  --nopublish \
  --output releases/Release-2.0.0.yml
```

`--output` depends on `--nopublish`, so one invocation either writes a file or publishes a candidate, never both. The parent directory is not created — it must exist first.

To publish and keep a copy on disk, generate as normal and then fetch it:

```bash
sfp releasecandidate fetch -n core:Release-2.0.0 --repository myorg/myrepo -o releases/Release-2.0.0.yml
```

## When generation fails

* **No package resolved to an artifact.** Generation fails rather than publishing an empty release. Usually the git tags exist but the artifacts were never published to the registry — check the build log for npm-validation warnings. When migrating a project onto sfp server, also confirm the [npm registry integration](/flxbl/sfp/collaborate/integrations.md) is configured: without it the external registry is never consulted and pre-migration artifacts are reported missing. See [How versions and artifacts are resolved](#how-versions-and-artifacts-are-resolved).
* **A package named in `--include-package` did not resolve.**
* **No repository identifier**, and neither `GITHUB_REPOSITORY` nor `GITLAB_REPOSITORY` is set, while publishing.
* **No promotion target.** The release config does not set `promotePackagesBeforeDeploymentToOrg` and no active release environment is registered for the repository.

{% hint style="info" %}
Coming from v50: this command was `sfp releasedefinition:generate`, which wrote definitions to a directory. It was removed in v51, along with `-d, --directory`, `--branchname`, `--nopush` and `--forcepush`. `-b, --branch` also changed meaning — it records a branch against the candidate rather than naming a branch to commit the definition into. See [Upgrading the CLI from v50 to v51](/flxbl/sfp/getting-started/docker-images/sfp-pro/upgrading-v50-to-v51.md).
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.flxbl.io/flxbl/sfp/releasing-artifacts/generating-a-release-definition.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
