diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7b463a36..a076d924 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -143,6 +143,8 @@ jobs: matrix: os: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.os }} + permissions: + contents: read name: Test current action steps: - uses: actions/checkout@v4 @@ -160,6 +162,8 @@ jobs: test-runs-on-container: needs: docker-build runs-on: ubuntu-latest + permissions: + contents: read container: image: node:20.19.2 @@ -183,6 +187,8 @@ jobs: os: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.os }} name: Mock a release + permissions: + contents: read steps: - uses: actions/checkout@v4 with: @@ -202,6 +208,8 @@ jobs: os: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.os }} name: Mock a release in a different working directory + permissions: + contents: read steps: - name: Checkout directory we'll be running from uses: actions/checkout@v4 @@ -231,6 +239,8 @@ jobs: node-version: ['20.x', '22.x'] runs-on: ${{ matrix.os }} name: Test Node version preserved on ${{ matrix.os }} with Node ${{ matrix.node-version }} + permissions: + contents: read steps: - uses: actions/checkout@v4 with: @@ -266,4 +276,33 @@ jobs: echo "ERROR: Node version changed from ${{ steps.node_before.outputs.VERSION }} to $VERSION_AFTER" exit 1 fi + echo "SUCCESS: Node version preserved" + + test-manual-commit-range: + needs: docker-build + + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + name: Test manual commit range + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Create a release with manual commit range + uses: ./ + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_LOG_LEVEL: debug + MOCK: true + with: + environment: production + set_commits: manual + repo: getsentry/action-release + commit: ${{ github.sha }} + previous_commit: ${{ github.sha }} diff --git a/CHANGELOG.md b/CHANGELOG.md index bf3638f9..f8bfbfe7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 3.4.0 + +- feat: Add support for setting manual commit range (#291) by @andreiborza + +Work in this release was contributed by @trevorkwhite. Thank you for your contribution! + ## 3.3.0 ### Various fixes & improvements @@ -7,6 +13,8 @@ - chore: pin cache action (#290) by @saibotk - chore: Set docker tag for master [skip ci] (ae1d1cd5) by @getsantry[bot] +Work in this release was contributed by @saibotk. Thank you for your contribution! + ## 3.2.0 ### Various fixes & improvements diff --git a/README.md b/README.md index 658ee538..8451d861 100644 --- a/README.md +++ b/README.md @@ -58,66 +58,78 @@ Adding the following to your workflow will create a new Sentry release and tell #### Environment Variables -|name|description|default| -|---|---|---| -|`SENTRY_AUTH_TOKEN`|**[Required]** Authentication token for Sentry. See [installation](#create-a-sentry-internal-integration).|-| -|`SENTRY_ORG`|**[Required]** The slug of the organization name in Sentry.|-| -|`SENTRY_PROJECT`|The slug of the project name in Sentry. One of `SENTRY_PROJECT` or `projects` is required.|-| -|`SENTRY_URL`|The URL used to connect to Sentry. (Only required for [Self-Hosted Sentry](https://develop.sentry.dev/self-hosted/))|`https://sentry.io/`| +| name | description | default | +| ------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------- | +| `SENTRY_AUTH_TOKEN` | **[Required]** Authentication token for Sentry. See [installation](#create-a-sentry-internal-integration). | - | +| `SENTRY_ORG` | **[Required]** The slug of the organization name in Sentry. | - | +| `SENTRY_PROJECT` | The slug of the project name in Sentry. One of `SENTRY_PROJECT` or `projects` is required. | - | +| `SENTRY_URL` | The URL used to connect to Sentry. (Only required for [Self-Hosted Sentry](https://develop.sentry.dev/self-hosted/)) | `https://sentry.io/` | #### Parameters -| name | description |default| -|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---| -| `environment` | Set the environment for this release. E.g. "production" or "staging". Omit to skip adding deploy to release. |-| -| `sourcemaps` | Space-separated list of paths to JavaScript sourcemaps. Omit to skip uploading sourcemaps. |-| -| `inject` | Injects Debug IDs into source files and source maps to ensure proper un-minifcation of your stacktraces. Does nothing if `sourcemaps` was not set. |`true`| -| `finalize` | When false, omit marking the release as finalized and released. |`true`| -| `ignore_missing` | When the flag is set and the previous release commit was not found in the repository, will create a release with the default commits count instead of failing the command. |`false`| -| `ignore_empty` | When the flag is set, command will not fail and just exit silently if no new commits for a given release have been found. |`false`| -| `dist` | Unique identifier for the distribution, used to further segment your release. Usually your build number. |-| -| `started_at` | Unix timestamp of the release start date. Omit for current time. |-| -| `release` | Identifier that uniquely identifies the releases. Should match the `release` property in your Sentry SDK init call if one was set._Note: the `refs/tags/` prefix is automatically stripped when `version` is `github.ref`._ |${{ github.sha }}| -| `version` | Deprecated: Use `release` instead. |${{ github.sha }}| -| `release_prefix` | Value prepended to auto-generated version. For example "v". |-| -| `version_prefix` | Deprecated: Use `release_prefix` instead. |-| -| `set_commits` | Specify whether to set commits for the release. Either "auto" or "skip". |"auto"| -| `projects` | Space-separated list of paths of projects. When omitted, falls back to the environment variable `SENTRY_PROJECT` to determine the project. |-| -| `url_prefix` | Adds a prefix to source map urls after stripping them. |-| -| `strip_common_prefix` | Will remove a common prefix from uploaded filenames. Useful for removing a path that is build-machine-specific. |`false`| -| `working_directory` | Directory to collect sentry release information from. Useful when collecting information from a non-standard checkout directory. |-| -| `disable_telemetry` | The action sends telemetry data and crash reports to Sentry. This helps us improve the action. You can turn this off by setting this flag. |`false`| -| `disable_safe_directory` | The action needs access to the repo it runs in. For that we need to configure git to mark the repo as a safe directory. You can turn this off by setting this flag. |`false`| - +| name | description | default | +| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | +| `environment` | Set the environment for this release. E.g. "production" or "staging". Omit to skip adding deploy to release. | - | +| `sourcemaps` | Space-separated list of paths to JavaScript sourcemaps. Omit to skip uploading sourcemaps. | - | +| `inject` | Injects Debug IDs into source files and source maps to ensure proper un-minifcation of your stacktraces. Does nothing if `sourcemaps` was not set. | `true` | +| `finalize` | When false, omit marking the release as finalized and released. | `true` | +| `ignore_missing` | When the flag is set and the previous release commit was not found in the repository, will create a release with the default commits count instead of failing the command. | `false` | +| `ignore_empty` | When the flag is set, command will not fail and just exit silently if no new commits for a given release have been found. | `false` | +| `dist` | Unique identifier for the distribution, used to further segment your release. Usually your build number. | - | +| `started_at` | Unix timestamp of the release start date. Omit for current time. | - | +| `release` | Identifier that uniquely identifies the releases. Should match the `release` property in your Sentry SDK init call if one was set. _Note: the `refs/tags/` prefix is automatically stripped when `version` is `github.ref`._ | ${{ github.sha }} | +| `version` | Deprecated: Use `release` instead. | ${{ github.sha }} | +| `release_prefix` | Value prepended to auto-generated version. For example "v". | - | +| `version_prefix` | Deprecated: Use `release_prefix` instead. | - | +| `set_commits` | Specify whether to set commits for the release. When "manual", you need to provide the repository, commit, and previous commit. One of "auto", "skip" or "manual". | "auto" | +| `repo` | The repository to set commits for in "repo-owner/repo-name" format. Only used when `set_commits` is "manual". | - | +| `commit` | The commit SHA of the current release you are creating. Only used when `set_commits` is "manual". | - | +| `previous_commit` | The commit SHA of the previous release. Required when `set_commits` is "manual". | - | +| `projects` | Space-separated list of paths of projects. When omitted, falls back to the environment variable `SENTRY_PROJECT` to determine the project. | - | +| `url_prefix` | Adds a prefix to source map urls after stripping them. | - | +| `strip_common_prefix` | Will remove a common prefix from uploaded filenames. Useful for removing a path that is build-machine-specific. | `false` | +| `working_directory` | Directory to collect sentry release information from. Useful when collecting information from a non-standard checkout directory. | - | +| `disable_telemetry` | The action sends telemetry data and crash reports to Sentry. This helps us improve the action. You can turn this off by setting this flag. | `false` | +| `disable_safe_directory` | The action needs access to the repo it runs in. For that we need to configure git to mark the repo as a safe directory. You can turn this off by setting this flag. | `false` | ### Examples - Create a new Sentry release for the `production` environment, inject Debug IDs into JavaScript source files and source maps and upload them from the `./dist` directory. - ```yaml - - uses: getsentry/action-release@v3 - with: - environment: 'production' - sourcemaps: './dist' - ``` + ```yaml + - uses: getsentry/action-release@v3 + with: + environment: 'production' + sourcemaps: './dist' + ``` - Create a new Sentry release for the `production` environment of your project at version `v1.0.1`. - ```yaml - - uses: getsentry/action-release@v3 - with: - environment: 'production' - release: 'v1.0.1' - ``` + ```yaml + - uses: getsentry/action-release@v3 + with: + environment: 'production' + release: 'v1.0.1' + ``` - Create a new Sentry release for [Self-Hosted Sentry](https://develop.sentry.dev/self-hosted/) - ```yaml - - uses: getsentry/action-release@v3 - env: - SENTRY_URL: https://sentry.example.com/ - ``` + ```yaml + - uses: getsentry/action-release@v3 + env: + SENTRY_URL: https://sentry.example.com/ + ``` + +- Manually specify the commit range for the release. + ```yaml + - uses: getsentry/action-release@v3 + with: + set_commits: manual + repo: your-org/your-repo + commit: ${{ github.sha }} + previous_commit: + ``` ## Contributing @@ -135,36 +147,36 @@ Suggestions and issues can be posted on the repository's - Forgetting to include the required environment variables (`SENTRY_AUTH_TOKEN`, `SENTRY_ORG`, and `SENTRY_PROJECT`), yields an error that looks like: - ```text - Environment variable SENTRY_ORG is missing an organization slug - ``` + ```text + Environment variable SENTRY_ORG is missing an organization slug + ``` - Building and running this action locally on an unsupported environment yields an error that looks like: - ```text - Syntax error: end of file unexpected (expecting ")") - ``` + ```text + Syntax error: end of file unexpected (expecting ")") + ``` - When adding the action, make sure to first check out your repo with `actions/checkout@v4`. -Otherwise, it could fail at the `propose-version` step with the message: + Otherwise, it could fail at the `propose-version` step with the message: - ```text - error: Could not automatically determine release name - ``` + ```text + error: Could not automatically determine release name + ``` - In `actions/checkout@v4` the default fetch depth is 1. If you're getting the error message: - ```text - error: Could not find the SHA of the previous release in the git history. Increase your git clone depth. - ``` + ```text + error: Could not find the SHA of the previous release in the git history. Increase your git clone depth. + ``` - you can fetch all history for all branches and tags by setting the `fetch-depth` to zero like so: + you can fetch all history for all branches and tags by setting the `fetch-depth` to zero like so: - ```yaml - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ``` + ```yaml + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + ``` - Not finding the repository @@ -176,12 +188,12 @@ Otherwise, it could fail at the `propose-version` step with the message: Ensure you use `actions/checkout` before running the action ```yaml - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: getsentry/action-release@v3 - with: - environment: 'production' - release: 'v1.0.1' - ``` + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: getsentry/action-release@v3 + with: + environment: 'production' + release: 'v1.0.1' + ``` diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index fb6abdc2..aa59ae77 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -11,6 +11,7 @@ import { getProjects, getUrlPrefixOption, getWorkingDirectory, + getSetCommitsManualOptions, } from '../src/options'; describe('options', () => { @@ -196,13 +197,37 @@ describe('options', () => { process.env['INPUT_SET_COMMITS'] = 'skip'; expect(getSetCommitsOption()).toBe('skip'); }); + it('manual', () => { + process.env['INPUT_SET_COMMITS'] = 'manual'; + expect(getSetCommitsOption()).toBe('manual'); + }); it('bad option', () => { - const errorMessage = 'set_commits must be "auto" or "skip"'; + const errorMessage = 'set_commits must be "auto", "skip" or "manual"'; process.env['INPUT_SET_COMMITS'] = 'bad'; expect(() => getSetCommitsOption()).toThrow(errorMessage); }); }); + describe('getSetCommitsManualOptions', () => { + afterEach(() => { + delete process.env['INPUT_SET_COMMITS']; + delete process.env['INPUT_REPO']; + delete process.env['INPUT_COMMIT']; + delete process.env['INPUT_PREVIOUS_COMMIT']; + }); + it('manual', () => { + process.env['INPUT_SET_COMMITS'] = 'manual'; + process.env['INPUT_REPO'] = 'repo'; + process.env['INPUT_COMMIT'] = 'commit'; + process.env['INPUT_PREVIOUS_COMMIT'] = 'previous-commit'; + expect(getSetCommitsManualOptions()).toEqual({ + repo: 'repo', + commit: 'commit', + previousCommit: 'previous-commit', + }); + }); + }); + describe('getProjects', () => { afterEach(() => { delete process.env['SENTRY_PROJECT']; diff --git a/action.yml b/action.yml index 94b93e3b..b10ce2e9 100644 --- a/action.yml +++ b/action.yml @@ -76,7 +76,23 @@ inputs: set_commits: description: |- Specify whether to set commits for the release. - One of: "auto", "skip" + When "manual", you need to provide the repository, commit, and previous commit. + One of: "auto", "skip", "manual" + required: false + repo: + description: |- + The repository to set commits for in "repo-owner/repo-name" format. + Only used when "set_commits" is "manual". + required: false + commit: + description: |- + The commit SHA of the current release you are creating. + Only used when "set_commits" is "manual". + required: false + previous_commit: + description: |- + The commit SHA of the previous release. + Required when "set_commits" is "manual". required: false projects: description: |- @@ -142,13 +158,16 @@ runs: INPUT_VERSION_PREFIX: ${{ inputs.version_prefix }} INPUT_RELEASE_PREFIX: ${{ inputs.release_prefix }} INPUT_SET_COMMITS: ${{ inputs.set_commits }} + INPUT_REPO: ${{ inputs.repo }} + INPUT_COMMIT: ${{ inputs.commit }} + INPUT_PREVIOUS_COMMIT: ${{ inputs.previous_commit }} INPUT_PROJECTS: ${{ inputs.projects }} INPUT_URL_PREFIX: ${{ inputs.url_prefix }} INPUT_STRIP_COMMON_PREFIX: ${{ inputs.strip_common_prefix }} INPUT_WORKING_DIRECTORY: ${{ inputs.working_directory }} INPUT_DISABLE_TELEMETRY: ${{ inputs.disable_telemetry }} INPUT_DISABLE_SAFE_DIRECTORY: ${{ inputs.disable_safe_directory }} - uses: docker://ghcr.io/getsentry/action-release-image:master + uses: docker://ghcr.io/getsentry/action-release-image:ab-add-manual-commit-range # For actions running on macos or windows runners, we use a composite # action approach which allows us to install the arch specific sentry-cli @@ -204,6 +223,9 @@ runs: INPUT_VERSION_PREFIX: ${{ inputs.version_prefix }} INPUT_RELEASE_PREFIX: ${{ inputs.release_prefix }} INPUT_SET_COMMITS: ${{ inputs.set_commits }} + INPUT_REPO: ${{ inputs.repo }} + INPUT_COMMIT: ${{ inputs.commit }} + INPUT_PREVIOUS_COMMIT: ${{ inputs.previous_commit }} INPUT_PROJECTS: ${{ inputs.projects }} INPUT_URL_PREFIX: ${{ inputs.url_prefix }} INPUT_STRIP_COMMON_PREFIX: ${{ inputs.strip_common_prefix }} diff --git a/dist/index.js b/dist/index.js index 03f976d3..102c0ce7 100644 --- a/dist/index.js +++ b/dist/index.js @@ -122752,11 +122752,21 @@ const telemetry_1 = __nccwpck_require__(12417); if (setCommitsOption !== 'skip') { yield (0, telemetry_1.traceStep)('set-commits', () => __awaiter(void 0, void 0, void 0, function* () { core.debug(`Setting commits with option '${setCommitsOption}'`); - yield (0, cli_1.getCLI)().setCommits(release, { - auto: true, - ignoreMissing, - ignoreEmpty, - }); + if (setCommitsOption === 'auto') { + yield (0, cli_1.getCLI)().setCommits(release, { + auto: true, + ignoreMissing, + ignoreEmpty, + }); + } + else if (setCommitsOption === 'manual') { + const { repo, commit, previousCommit } = options.getSetCommitsManualOptions(); + if (!repo || !commit) { + throw new Error('Options `repo` and `commit` are required when `set_commits` is `manual`'); + } + yield (0, cli_1.getCLI)().setCommits(release, Object.assign({ auto: false, repo, + commit }, (previousCommit && { previousCommit }))); + } })); } Sentry.setTag('sourcemaps', sourcemaps.length > 0); @@ -122860,7 +122870,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getWorkingDirectory = exports.getUrlPrefixOption = exports.getProjects = exports.checkEnvironmentVariables = exports.getSetCommitsOption = exports.getBooleanOption = exports.getDist = exports.getSourcemaps = exports.getStartedAt = exports.getEnvironment = exports.getRelease = void 0; +exports.getWorkingDirectory = exports.getUrlPrefixOption = exports.getProjects = exports.checkEnvironmentVariables = exports.getSetCommitsManualOptions = exports.getSetCommitsOption = exports.getBooleanOption = exports.getDist = exports.getSourcemaps = exports.getStartedAt = exports.getEnvironment = exports.getRelease = void 0; const core = __importStar(__nccwpck_require__(42186)); const path_1 = __importDefault(__nccwpck_require__(71017)); const cli_1 = __nccwpck_require__(56733); @@ -122993,11 +123003,20 @@ const getSetCommitsOption = () => { return 'auto'; case 'skip': return 'skip'; + case 'manual': + return 'manual'; default: - throw Error('set_commits must be "auto" or "skip"'); + throw Error('set_commits must be "auto", "skip" or "manual"'); } }; exports.getSetCommitsOption = getSetCommitsOption; +const getSetCommitsManualOptions = () => { + const repo = core.getInput('repo'); + const commit = core.getInput('commit'); + const previousCommit = core.getInput('previous_commit'); + return { repo, commit, previousCommit }; +}; +exports.getSetCommitsManualOptions = getSetCommitsManualOptions; /** * Check for required environment variables. */ diff --git a/src/main.ts b/src/main.ts index daff77ce..0bf8ac44 100644 --- a/src/main.ts +++ b/src/main.ts @@ -50,11 +50,27 @@ withTelemetry( if (setCommitsOption !== 'skip') { await traceStep('set-commits', async () => { core.debug(`Setting commits with option '${setCommitsOption}'`); - await getCLI().setCommits(release, { - auto: true, - ignoreMissing, - ignoreEmpty, - }); + + if (setCommitsOption === 'auto') { + await getCLI().setCommits(release, { + auto: true, + ignoreMissing, + ignoreEmpty, + }); + } else if (setCommitsOption === 'manual') { + const { repo, commit, previousCommit } = options.getSetCommitsManualOptions(); + + if (!repo || !commit) { + throw new Error('Options `repo` and `commit` are required when `set_commits` is `manual`'); + } + + await getCLI().setCommits(release, { + auto: false, + repo, + commit, + ...(previousCommit && { previousCommit }), + }); + } }); } diff --git a/src/options.ts b/src/options.ts index a1afad80..77d23f36 100644 --- a/src/options.ts +++ b/src/options.ts @@ -127,7 +127,7 @@ export const getBooleanOption = (input: string, defaultValue: boolean): boolean throw Error(`${input} is not a boolean`); }; -export const getSetCommitsOption = (): 'auto' | 'skip' => { +export const getSetCommitsOption = (): 'auto' | 'skip' | 'manual' => { let setCommitOption = core.getInput('set_commits'); // default to auto if (!setCommitOption) { @@ -140,11 +140,20 @@ export const getSetCommitsOption = (): 'auto' | 'skip' => { return 'auto'; case 'skip': return 'skip'; + case 'manual': + return 'manual'; default: - throw Error('set_commits must be "auto" or "skip"'); + throw Error('set_commits must be "auto", "skip" or "manual"'); } }; +export const getSetCommitsManualOptions = (): { repo: string; commit: string; previousCommit: string } => { + const repo = core.getInput('repo'); + const commit = core.getInput('commit'); + const previousCommit = core.getInput('previous_commit'); + + return { repo, commit, previousCommit }; +}; /** * Check for required environment variables. */