diff --git a/README.md b/README.md index ecb4f1c..ead4b84 100644 --- a/README.md +++ b/README.md @@ -36,30 +36,38 @@ on: ### Inputs -| Input | Description | Default | Required | -| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | -------- | -| `appID` | ID of the GitHub App to manage the release system. | | `true` | -| `appPrivateKey` | Private key of the GitHub App to manage the release system. | | `true` | -| `files` | Comma-separated or newline-separated list of release files with optional "label:" prefix. | | `true` | -| `discordWebhook` | Discord webhook to post the release to. | `none` | `false` | -| `discussionCategory` | The category to use for the discussion. Defaults to "none" if not specified. | `none` | `false` | -| `draftRelease` | Whether or not the release should be a draft. Defaults to false if not specified. | `false` | `false` | -| `ghApiUrl` | The GitHub API URL to use. Defaults to the api. plus the repo domain if not specified. | `auto` | `false` | -| `ghReleaseNotes` | Whether or not to let GitHub auto-generate its release notes. Defaults to false if not specified. | `false` | `false` | -| `includeReleaseInfo` | Whether or not to include the asset hashes in a release.json file. Defaults to true if not specified. | `true` | `false` | -| `lastCommit` | The last commit hash to use for the release. Defaults to the commit that triggered the workflow if not specified. | `auto` | `false` | -| `latestRelease` | Whether or not the release should be marked as the latest release. Defaults to auto if not specified, which will be true unless this is a pre-release. | `auto` | `false` | -| `preRelease` | Whether or not the release is a pre-release. Inferred by the branch if not specified. | `auto` | `false` | -| `releaseBody` | A file containing the body of the release. Defaults to the commit changelog if not specified. | `auto` | `false` | -| `releaseChangeLimit` | The maximum number of changes to include in the release body. Defaults to 15 if not specified. Set to -1 to include all changes. | `15` | `false` | -| `releaseEnabled` | Whether or not the release should be created. Defaults to true if not specified. | `true` | `false` | -| `releaseName` | The title of the release. Defaults to "Build ${tagBase} (${branch})" if not specified. | `auto` | `false` | -| `saveMetadata` | Whether or not to save the offline release metadata to metadata.json. Defaults to false if not specified. | `false` | `false` | -| `tagBase` | The tag base to use for the release. Auto increment from the last tag will be used if not specified. | `auto` | `false` | -| `tagIncrement` | If the build tag should be incremented. Defaults to true if not specified and tag is a number. | `true` | `false` | -| `tagPrefix` | The prefix to use for the tag. Defaults to the branch if not specified. | `auto` | `false` | -| `tagSeparator` | The separator to use between the tag prefix and the tag base. Defaults to "-" if not specified. | `-` | `false` | -| `updateReleaseData` | Whether or not to update the release data in repository variable storage. Defaults to true if not specified | `true` | `false` | +| Input | Description | Default | Required | +| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | -------- | +| `appID` | ID of the GitHub App to manage the release system. | | `true` | +| `appPrivateKey` | Private key of the GitHub App to manage the release system. | | `true` | +| `files` | Comma-separated or newline-separated list of release files with optional "label:" prefix. | | `true` | +| `discordWebhook` | Discord webhook to post the release to. | `none` | `false` | +| `discussionCategory` | The category to use for the discussion. Defaults to "none" if not specified. | `none` | `false` | +| `draftRelease` | Whether or not the release should be a draft. Defaults to false if not specified. | `false` | `false` | +| `ghApiUrl` | The GitHub API URL to use. Defaults to the api. plus the repo domain if not specified. | `auto` | `false` | +| `ghReleaseNotes` | Whether or not to let GitHub auto-generate its release notes. Defaults to false if not specified. | `false` | `false` | +| `includeReleaseInfo` | Whether or not to include the asset hashes in a release.json file. Defaults to true if not specified. | `true` | `false` | +| `lastCommit` | The last commit hash to use for the release. Defaults to the commit that triggered the workflow if not specified. | `auto` | `false` | +| `latestRelease` | Whether or not the release should be marked as the latest release. Defaults to auto if not specified, which will be true unless this is a pre-release. | `auto` | `false` | +| `preRelease` | Whether or not the release is a pre-release. Inferred by the branch if not specified. | `auto` | `false` | +| `releaseBody` | A file containing the body of the release. Defaults to the commit changelog if not specified. | `auto` | `false` | +| `releaseBodyDependencyUsage` | Whether or not to include the dependency usage in the release body. Defaults to "none" if not specified. Currently accepts "java" or "nodejs". | `none` | `false` | +| `releaseBodyDependencyJavaArtifactId` | The artifact ID to use for the Java dependency usage. | `` | `false` | +| `releaseBodyDependencyJavaGroupId` | The group ID to use for the Java dependency usage. | `` | `false` | +| `releaseBodyDependencyJavaMavenRepo` | The Maven repo to use for Java dependency usage. | `` | `false` | +| `releaseBodyDependencyJavaVersion` | The version to use for Java dependency usage. | `` | `false` | +| `releaseBodyDependencyNodejsPackage` | The package to use for Node.js dependency usage. | `` | `false` | +| `releaseBodyDependencyNodejsVarName` | The variable name to use for Node.js dependency usage. | `` | `false` | +| `releaseBodyDependencyNodejsVersion` | The version to use for Node.js dependency usage. | `` | `false` | +| `releaseChangeLimit` | The maximum number of changes to include in the release body. Defaults to 15 if not specified. Set to -1 to include all changes. | `15` | `false` | +| `releaseEnabled` | Whether or not the release should be created. Defaults to true if not specified. | `true` | `false` | +| `releaseName` | The title of the release. Defaults to "Build ${tagBase} (${branch})" if not specified. | `auto` | `false` | +| `saveMetadata` | Whether or not to save the offline release metadata to metadata.json. Defaults to false if not specified. | `false` | `false` | +| `tagBase` | The tag base to use for the release. Auto increment from the last tag will be used if not specified. | `auto` | `false` | +| `tagIncrement` | If the build tag should be incremented. Defaults to true if not specified and tag is a number. | `true` | `false` | +| `tagPrefix` | The prefix to use for the tag. Defaults to the branch if not specified. | `auto` | `false` | +| `tagSeparator` | The separator to use between the tag prefix and the tag base. Defaults to "-" if not specified. | `-` | `false` | +| `updateReleaseData` | Whether or not to update the release data in repository variable storage. Defaults to true if not specified | `true` | `false` | ### Outputs diff --git a/action.yml b/action.yml index 372927e..5cad7e4 100644 --- a/action.yml +++ b/action.yml @@ -51,6 +51,31 @@ inputs: description: 'A file containing the body of the release. Defaults to the commit changelog if not specified.' required: false default: 'auto' + releaseBodyDependencyUsage: + description: 'Whether or not to include the dependency usage in the release body. Defaults to "none" if not specified. Currently accepts "java" or "nodejs".' + required: false + default: 'none' + releaseBodyDependencyJavaArtifactId: + description: 'The artifact ID to use for the Java dependency usage.' + required: false + releaseBodyDependencyJavaGroupId: + description: 'The group ID to use for the Java dependency usage.' + required: false + releaseBodyDependencyJavaMavenRepo: + description: 'The Maven repo to use for Java dependency usage.' + required: false + releaseBodyDependencyJavaVersion: + description: 'The version to use for Java dependency usage.' + required: false + releaseBodyDependencyNodejsPackage: + description: 'The package to use for Node.js dependency usage.' + required: false + releaseBodyDependencyNodejsVarName: + description: 'The variable name to use for Node.js dependency usage.' + required: false + releaseBodyDependencyNodejsVersion: + description: 'The version to use for Node.js dependency usage.' + required: false releaseChangeLimit: description: 'The maximum number of changes to include in the release body. Defaults to 15 if not specified. Set to -1 to include all changes.' required: false diff --git a/src/action/inputs.ts b/src/action/inputs.ts index 652c6da..e0f73b2 100644 --- a/src/action/inputs.ts +++ b/src/action/inputs.ts @@ -91,7 +91,8 @@ async function getRelease(inp: {api: OctokitApi, changes: Inputs.Change[], tag: const { owner, repo, branch } = repoData; - const body = await getReleaseBody({repoData, changes}); + const body_dependency_usage = getBodyDependencyUsage(); + const body = await getReleaseBody({repoData, changes, usageExamples: body_dependency_usage, tag}); const prerelease = await getPreRelease({repoData}); const name = getName({tag, branch}); const draft = core.getBooleanInput('draftRelease'); @@ -105,7 +106,7 @@ async function getRelease(inp: {api: OctokitApi, changes: Inputs.Change[], tag: const update_release_data = core.getBooleanInput('updateReleaseData'); console.log(`Using release name ${name} with prerelease: ${prerelease}, draft: ${draft}, generate release notes: ${generate_release_notes}, discussion category: ${discussion_category_name}, make latest: ${make_latest}, include release info: ${info}`); - return { name, body, prerelease, draft, generate_release_notes, discussion_category_name, make_latest, info, hook, enabled, metadata, update_release_data }; + return { name, body, prerelease, draft, generate_release_notes, discussion_category_name, make_latest, info, hook, enabled, metadata, update_release_data, body_dependency_usage }; } async function getSuccess(inp: {api: OctokitApi, repoData: Repo}): Promise { @@ -200,8 +201,8 @@ async function getChanges(inp: {api: OctokitApi, prevRelease: PreviousRelease, r } } -async function getReleaseBody(inp: {repoData: Repo, changes: Inputs.Change[]}): Promise { - const { repoData, changes } = inp; +async function getReleaseBody(inp: {repoData: Repo, changes: Inputs.Change[], usageExamples: Inputs.Release['body_dependency_usage'], tag: Inputs.Tag}): Promise { + const { repoData, changes, usageExamples, tag } = inp; const bodyPath = core.getInput('releaseBodyPath'); @@ -210,13 +211,15 @@ async function getReleaseBody(inp: {repoData: Repo, changes: Inputs.Change[]}): if (changes.length === 0) { return ''; } + + let body = ''; const { owner, repo, url } = repoData; const firstCommit = changes[0].commit.slice(0, 7); const lastCommit = changes[changes.length - 1].commit.slice(0, 7); const diffURL = `${url}/${owner}/${repo}/compare/${firstCommit}^...${lastCommit}`; - let changelog = `## Changes: [\`${firstCommit}...${lastCommit}\`](${diffURL})${os.EOL}`; + body += `## Changes: [\`${firstCommit}...${lastCommit}\`](${diffURL})${os.EOL}`; const changeLimit = core.getInput('releaseChangeLimit'); let truncatedChanges = 0; @@ -241,14 +244,97 @@ async function getReleaseBody(inp: {repoData: Repo, changes: Inputs.Change[]}): break; } const sha = change.commit.slice(0, 7); - changelog += `- ${markdownEscape(change.summary)} ([\`${sha}\`](${url}/${owner}/${repo}/commit/${sha})) by ${markdownEscape(authors)}${os.EOL}`; + body += `- ${markdownEscape(change.summary)} ([\`${sha}\`](${url}/${owner}/${repo}/commit/${sha})) by ${markdownEscape(authors)}${os.EOL}`; } if (truncatedChanges > 0) { - changelog += `... and ${truncatedChanges} more${os.EOL}`; + body += `... and ${truncatedChanges} more${os.EOL}`; + } + + if (usageExamples.type !== 'none') { + switch (usageExamples.type) { + case 'java': + if (!usageExamples.java) break; + if (!usageExamples.java.group_id || !usageExamples.java.artifact_id) break; + + body += `## Usage${os.EOL}`; + + let { group_id, artifact_id, version: javaVersion, maven_repo } = usageExamples.java; + + if (!javaVersion) { + javaVersion = tag.base; + }; + + body += `### Gradle (Kotlin DSL)${os.EOL}`; + body += '```kotlin' + os.EOL; + if (maven_repo) { + body += `repositories {` + os.EOL; + body += ` maven("${maven_repo}")` + os.EOL; + body += `}` + os.EOL; + body += os.EOL; + } + body += `implementation("${group_id}:${artifact_id}:${javaVersion}")` + os.EOL; + body += '```' + os.EOL; + + body += `### Gradle (Groovy)${os.EOL}`; + body += '```groovy' + os.EOL; + if (maven_repo) { + body += `repositories {` + os.EOL; + body += ` maven { url "${maven_repo}" }` + os.EOL; + body += `}` + os.EOL; + body += os.EOL; + } + body += `implementation '${group_id}:${artifact_id}:${javaVersion}'` + os.EOL; + body += '```' + os.EOL; + + body += `### Maven${os.EOL}`; + body += '```xml' + os.EOL; + if (maven_repo) { + body += `` + os.EOL; + body += ` ` + os.EOL; + body += ` ${repoData.owner}` + os.EOL; + body += ` ${maven_repo}` + os.EOL; + body += ` ` + os.EOL; + body += `` + os.EOL; + body += os.EOL; + } + body += `` + os.EOL; + body += ` ${group_id}` + os.EOL; + body += ` ${artifact_id}` + os.EOL; + body += ` ${javaVersion}` + os.EOL; + body += `` + os.EOL; + body += '```' + os.EOL; + + break; + case 'nodejs': + if (!usageExamples.nodejs) break; + if (!usageExamples.nodejs.package || !usageExamples.nodejs.var_name) break; + + body += `## Usage${os.EOL}`; + + let { package: packageName, version: nodejsVersion, var_name } = usageExamples.nodejs; + + if (!nodejsVersion) { + nodejsVersion = tag.base; + }; + + body += `### Install${os.EOL}`; + body += '```bash' + os.EOL; + body += `npm install ${packageName}@${nodejsVersion}` + os.EOL; + body += `yarn add ${packageName}@${nodejsVersion}` + os.EOL; + body += `pnpm add ${packageName}@${nodejsVersion}` + os.EOL; + body += '```' + os.EOL; + + body += `### Import${os.EOL}`; + body += '```js' + os.EOL; + body += `const ${var_name} = require('${packageName}');` + os.EOL; + body += `import ${var_name} from '${packageName}';` + os.EOL; + body += '```' + os.EOL; + break; + } } - return changelog; + return body; } return fs.readFileSync(bodyPath, { encoding: 'utf-8' }); @@ -318,4 +404,35 @@ function getMakeLatest(inp: {prerelease: boolean, success: boolean}): "true" | " } return make_latest; +} + +function getBodyDependencyUsage(): Inputs.Release['body_dependency_usage'] { + const dependency_usage_examples = core.getInput('releaseBodyDependencyUsage'); + + switch (dependency_usage_examples) { + case "java": + return { + type: "java", + java: { + group_id: core.getInput('releaseBodyDependencyJavaArtifactId') || undefined, + artifact_id: core.getInput('releaseBodyDependencyJavaGroupId') || undefined, + version: core.getInput('releaseBodyDependencyJavaVersion') || undefined, + maven_repo: core.getInput('releaseBodyDependencyJavaMavenRepo') || undefined + } + } + case "nodejs": + return { + type: "nodejs", + nodejs: { + package: core.getInput('releaseBodyDependencyNodejsPackage') || undefined, + version: core.getInput('releaseBodyDependencyNodejsVersion') || undefined, + var_name: core.getInput('releaseBodyDependencyNodejsVarName') || undefined + } + } + case "none": + default: + return { + type: "none" + }; + } } \ No newline at end of file diff --git a/src/types/inputs.ts b/src/types/inputs.ts index b30fd8d..1678dbd 100644 --- a/src/types/inputs.ts +++ b/src/types/inputs.ts @@ -41,6 +41,20 @@ export namespace Inputs { readonly hook: string | undefined; readonly metadata: boolean; readonly update_release_data: boolean; + readonly body_dependency_usage: { + type: "java" | "nodejs" | "none"; + java?: { + group_id?: string; + artifact_id?: string; + version?: string; + maven_repo?: string; + }; + nodejs?: { + package?: string; + version?: string; + var_name?: string; + }; + }; } }