diff --git a/README.md b/README.md index ab1f1e4..a233076 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,15 @@ GitHub action to generate a CycloneDX SBOM for Go modules. ### `version` -**Required** The version of cyclonedx-gomod to use. +**Required** The version of cyclonedx-gomod to use. Can be a version range, in which case the latest version matching the range is chosen. -Must either be an [existing semantic version](https://github.com/CycloneDX/cyclonedx-gomod/releases) (e.g. `v0.8.1`, `0.8.1`) or `latest`. +Must either be an [existing semantic version](https://github.com/CycloneDX/cyclonedx-gomod/releases) (e.g. `v0.8.1`, `0.8.1`), [version range](https://github.com/npm/node-semver#ranges) or `latest`. > ⚠ Only versions `>= v0.8.1` are supported. Specifying versions below that will cause the workflow to fail. > Using `latest` is generally not recommended and will produce a warning, as it may fail your workflow > unexpectedly due to breaking changes in newer *cyclonedx-gomod* versions. +> As of v0.3.0, version ranges are supported. Instead of `latest`, consider using `^v0`, `^v0.8` or similar instead. ### `include-stdlib` @@ -61,15 +62,15 @@ Type of the main component. Default `'application'`. ```yaml - name: Generate SBOM JSON - uses: CycloneDX/gh-gomod-generate-sbom@main + uses: CycloneDX/gh-gomod-generate-sbom@v0.3.0 with: json: true output: bom.json resolve-licenses: true - version: v0.8.1 + version: ^v0 - name: Generate SBOM XML - uses: CycloneDX/gh-gomod-generate-sbom@main + uses: CycloneDX/gh-gomod-generate-sbom@v0.3.0 with: output: bom.xml resolve-licenses: true diff --git a/action.yml b/action.yml index cacad3a..673e6f7 100644 --- a/action.yml +++ b/action.yml @@ -43,7 +43,7 @@ inputs: default: application required: false version: - description: The version of cyclonedx-gomod to use + description: The version of cyclonedx-gomod to use. Can be a version range, in which case the latest version matching the range is chosen required: true runs: using: 'node12' diff --git a/index.js b/index.js index 701a49e..24ffd20 100644 --- a/index.js +++ b/index.js @@ -41,6 +41,7 @@ const input = { }; const baseDownloadUrl = 'https://github.com/CycloneDX/cyclonedx-gomod/releases/download'; +const minimumSupportedVersion = 'v0.8.1'; function buildDownloadUrl(version) { let fileExtension = "tar.gz"; @@ -64,13 +65,27 @@ async function getLatestReleaseVersion(httpClient) { const responseJson = await httpClient.getJson('https://api.github.com/repos/CycloneDX/cyclonedx-gomod/releases/latest'); if (responseJson === null) { // HTTP 404 throw new Error('Fetching latest release of cyclonedx-gomod failed: not found'); + } else if (responseJson.statusCode !== 200) { + throw new Error(`Unexpected response status: ${responseJson.statusCode}`); } - if (responseJson.statusCode !== 200) { + const version = responseJson.result.tag_name; + core.info(`Latest version is ${version}`); + return version; +} + +async function getReleaseVersionMatchingRange(httpClient, range) { + core.info(`Determining latest release version of cyclonedx-gomod satisfying "${range}"`); + const responseJson = await httpClient.getJson('https://api.github.com/repos/CycloneDX/cyclonedx-gomod/releases'); + if (responseJson === null) { // HTTP 404 + throw new Error('Fetching latest release of cyclonedx-gomod failed: not found'); + } else if (responseJson.statusCode !== 200) { throw new Error(`Unexpected response status: ${responseJson.statusCode}`); } - return responseJson.result.tag_name; + const matched = semver.maxSatisfying(responseJson.result.map((release) => release.tag_name), range); + core.info(`Latest release version matching "${range}" is: ${matched}`); + return matched; } async function install(version) { @@ -100,11 +115,17 @@ async function run() { let versionToInstall = input.version; if (versionToInstall.toLowerCase() === 'latest') { - core.warning('Using version "latest" is not recommended!'); + core.warning('Using version "latest" is not recommended, please use version ranges instead!'); versionToInstall = await getLatestReleaseVersion(httpClient); } else { - if (semver.lt(versionToInstall, 'v0.8.1')) { - throw new Error('cyclonedx-gomod versions below v0.8.1 are not supported'); + if (!semver.validRange(versionToInstall)) { + throw new Error('version must be a valid version range, see https://github.com/npm/node-semver#advanced-range-syntax') + } + + versionToInstall = await getReleaseVersionMatchingRange(httpClient, versionToInstall); + + if (semver.lt(versionToInstall, minimumSupportedVersion)) { + throw new Error(`cyclonedx-gomod versions below ${minimumSupportedVersion} are not supported`); } }