diff --git a/README.md b/README.md index 7233fe4b..d3039a2e 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,8 @@ steps: | `proxy-server` | Configure a proxy server in the form of `:` e.g. `proxy-host.com:8080` | | `skip-github-release` | If `true`, do not attempt to create releases. This is useful if splitting release tagging from PR creation. | | `skip-github-pull-request` | If `true`, do not attempt to create release pull requests. This is useful if splitting release tagging from PR creation. | -| `skip-labeling` | If `true`, do not attempt to label the PR. | +| `skip-labeling` | If `true`, do not attempt to label the PR. | +| `dry-run` | If `true`, the action outputs pending releases and/or pending pull requests, but does not create them. | ## GitHub Credentials @@ -94,7 +95,7 @@ the `token` configuration option. If your repository is in an organization, you may need to [permit github actions to create an approve PRs](https://stackoverflow.com/questions/72376229). -> [!WARNING] +> [!WARNING] > If using GitHub Actions, you will need to specify a `token` for your workflows to run on > Release Please's releases and PRs. See [the heading below](#other-actions-on-release-please-prs). @@ -167,13 +168,16 @@ New types of releases can be [added here](https://github.com/googleapis/release- > Properties that are available after the action executed. -| output | description | -| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `releases_created` | `true` if any release was created, `false` otherwise | -| `paths_released` | A JSON string of the array of paths that had releases created (`[]` if nothing was released) | -| `prs_created` | `true` if any pull request was created or updated | -| `pr` | A JSON string of the [PullRequest object](https://github.com/googleapis/release-please/blob/main/src/pull-request.ts#L15) (unset if no release created) | -| `prs` | A JSON string of the array of [PullRequest objects](https://github.com/googleapis/release-please/blob/main/src/pull-request.ts#L15) (unset if no release created) | +| output | description | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `releases_created` | `true` if any release was created, `false` otherwise. | +| `paths_released` | A JSON string of the array of paths that had releases created (`[]` if nothing was released). | +| `releases_pending` | `true` if any candidate release is pending (when running in dry-run mode). | +| `paths_to_release` | A JSON string of the array of paths that have pending candidate releases (when running in dry-run mode). | +| `prs_created` | `true` if any pull request was created or updated. | +| `prs_pending` | `true` if any candidate pull request is pending (when running in dry-run mode). | +| `pr` | A JSON string of the [PullRequest object](https://github.com/googleapis/release-please/blob/main/src/pull-request.ts#L15) (in dry-run [ReleasePullRequest objects](https://github.com/googleapis/release-please/blob/main/src/release-pull-request.ts#L20), unset otherwise) | +| `prs` | A JSON string of the array of [PullRequest objects](https://github.com/googleapis/release-please/blob/main/src/pull-request.ts#L15) (in dry-run [ReleasePullRequest objects](https://github.com/googleapis/release-please/blob/main/src/release-pull-request.ts#L20), unset otherwise) | ### Root component outputs diff --git a/action.yml b/action.yml index add125a2..38a4531d 100644 --- a/action.yml +++ b/action.yml @@ -74,6 +74,10 @@ inputs: description: 'The version to release as.' required: false default: '' + dry-run: + description: 'if set to true, the action lists build releases and/or PRs but does not create them' + required: false + default: false runs: using: 'node20' main: 'dist/index.js' diff --git a/src/index.ts b/src/index.ts index 480dd3e4..e16b07cf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,7 +13,7 @@ // limitations under the License. import * as core from '@actions/core'; -import {GitHub, Manifest, CreatedRelease, PullRequest, VERSION} from 'release-please'; +import {GitHub, Manifest, CreatedRelease, PullRequest, VERSION, CandidateRelease, ReleasePullRequest} from 'release-please'; const DEFAULT_CONFIG_FILE = 'release-please-config.json'; const DEFAULT_MANIFEST_FILE = '.release-please-manifest.json'; @@ -45,6 +45,7 @@ interface ActionInputs { changelogHost: string; versioningStrategy?: string; releaseAs?: string; + dryRun?: boolean; } function parseInputs(): ActionInputs { @@ -69,6 +70,7 @@ function parseInputs(): ActionInputs { changelogHost: core.getInput('changelog-host') || DEFAULT_GITHUB_SERVER_URL, versioningStrategy: getOptionalInput('versioning-strategy'), releaseAs: getOptionalInput('release-as'), + dryRun: getOptionalBooleanInput('dry-run'), }; return inputs; } @@ -137,17 +139,29 @@ export async function main(fetchOverride?: any) { core.info(`Running release-please version: ${VERSION}`) const inputs = parseInputs(); const github = await getGitHubInstance(inputs, fetchOverride); + const needsManifest = !inputs.skipGitHubRelease || !inputs.skipGitHubPullRequest; + const manifest = needsManifest + ? await loadOrBuildManifest(github, inputs) + : undefined; - if (!inputs.skipGitHubRelease) { - const manifest = await loadOrBuildManifest(github, inputs); - core.debug('Creating releases'); - outputReleases(await manifest.createReleases()); + if (!inputs.skipGitHubRelease && manifest) { + if (inputs.dryRun) { + core.debug('Listing pending releases'); + outputCandidateReleases(await manifest.buildReleases()); + } else { + core.debug('Creating releases'); + outputReleases(await manifest.createReleases()); + } } - if (!inputs.skipGitHubPullRequest) { - const manifest = await loadOrBuildManifest(github, inputs); - core.debug('Creating pull requests'); - outputPRs(await manifest.createPullRequests()); + if (!inputs.skipGitHubPullRequest && manifest) { + if (inputs.dryRun) { + core.debug('Listing pending pull requests'); + outputCandidatePRs(await manifest.buildPullRequests()); + } else { + core.debug('Creating pull requests'); + outputPRs(await manifest.createPullRequests()); + } } } @@ -216,6 +230,35 @@ function outputReleases(releases: (CreatedRelease | undefined)[]) { core.setOutput('paths_released', JSON.stringify(pathsReleased)); } +function outputCandidateReleases(releases: CandidateRelease[]) { + releases = releases.filter(release => release !== undefined); + const pathsReleased = []; + core.setOutput('releases_pending', releases.length > 0); + if (releases.length) { + for (const release of releases) { + if (!release) { + continue; + } + const path = release.path || '.'; + if (path) { + pathsReleased.push(path); + // If the special root release is set (representing project root) + // and this is explicitly a manifest release, set the release_created boolean. + setPathOutput(path, 'release_pending', true); + } + if (release.tag) { + // Historically tagName was output as tag_name, keep this + // consistent to avoid breaking change: + setPathOutput(path, 'tag_name', release.tag.toString()); + setPathOutput(path, 'body', release.notes) + } + } + } + // Paths of all releases that were created, so that they can be passed + // to matrix in next step: + core.setOutput('paths_to_release', JSON.stringify(pathsReleased)); +} + function outputPRs(prs: (PullRequest | undefined)[]) { prs = prs.filter(pr => pr !== undefined); core.setOutput('prs_created', prs.length > 0); @@ -225,6 +268,15 @@ function outputPRs(prs: (PullRequest | undefined)[]) { } } +function outputCandidatePRs(prs: ReleasePullRequest[]) { + prs = prs.filter(pr => pr !== undefined); + core.setOutput('prs_pending', prs.length > 0); + if (prs.length) { + core.setOutput('pr', prs[0]); + core.setOutput('prs', JSON.stringify(prs)); + } +} + if (require.main === module) { main().catch(err => { core.setFailed(`release-please failed: ${err.message}`)