fix: Only pass urlPrefix to sentry-cli if it's not empty (#275)

* fix: Only passes `urlPrefix` to sentry-cli if it's not empty

Also lets sentry-cli handle uploading source maps for multiple projects.

* Update prettier settings and reformat
This commit is contained in:
Andrei
2025-03-21 11:35:29 +09:00
committed by GitHub
parent f176a77e46
commit a154094c90
8 changed files with 76 additions and 107 deletions
+2 -2
View File
@@ -1,11 +1,11 @@
{
"printWidth": 80,
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": false,
"bracketSpacing": true,
"arrowParens": "avoid",
"overrides": [
{
+18 -26
View File
@@ -1,4 +1,4 @@
import {execSync} from 'child_process';
import { execSync } from 'child_process';
import * as path from 'path';
import * as process from 'process';
import {
@@ -29,9 +29,7 @@ describe('options', () => {
test('should throw an error when option type is not a boolean', () => {
process.env['INPUT_FINALIZE'] = 'error';
expect(() => getBooleanOption(option, defaultValue)).toThrow(
errorMessage
);
expect(() => getBooleanOption(option, defaultValue)).toThrow(errorMessage);
});
test('should return defaultValue if option is omitted', () => {
@@ -84,8 +82,7 @@ describe('options', () => {
});
describe('getStartedAt', () => {
const errorMessage =
'started_at not in valid format. Unix timestamp or ISO 8601 date expected';
const errorMessage = 'started_at not in valid format. Unix timestamp or ISO 8601 date expected';
afterEach(() => {
delete process.env['INPUT_STARTED_AT'];
});
@@ -121,10 +118,10 @@ describe('options', () => {
});
describe.each([
{release: 'INPUT_RELEASE', prefix: 'INPUT_RELEASE_PREFIX'},
{release: 'INPUT_VERSION', prefix: 'INPUT_VERSION_PREFIX'},
{release: 'INPUT_RELEASE', prefix: 'INPUT_VERSION_PREFIX'},
{release: 'INPUT_VERSION', prefix: 'INPUT_RELEASE_PREFIX'},
{ release: 'INPUT_RELEASE', prefix: 'INPUT_RELEASE_PREFIX' },
{ release: 'INPUT_VERSION', prefix: 'INPUT_VERSION_PREFIX' },
{ release: 'INPUT_RELEASE', prefix: 'INPUT_VERSION_PREFIX' },
{ release: 'INPUT_VERSION', prefix: 'INPUT_RELEASE_PREFIX' },
])(`getRelease`, params => {
const MOCK_VERSION = 'releases propose-version';
@@ -250,9 +247,7 @@ describe('options', () => {
it('gets the working directory url and prefixes it with the `GITHUB_WORKSPACE`', () => {
process.env['GITHUB_WORKSPACE'] = '/repo/root';
process.env['INPUT_WORKING_DIRECTORY'] = '/some/working/directory';
expect(getWorkingDirectory()).toEqual(
'/repo/root/some/working/directory'
);
expect(getWorkingDirectory()).toEqual('/repo/root/some/working/directory');
});
it('should default to `GITHUB_WORKSPACE` even if no direcotry is passed', () => {
@@ -264,19 +259,16 @@ describe('options', () => {
// shows how the runner will run a javascript action with env / stdout protocol
test('test runs', () => {
const output = execSync(
`node ${path.join(__dirname, '..', 'dist', 'index.js')}`,
{
env: {
...process.env,
INPUT_ENVIRONMENT: 'production',
MOCK: 'true',
SENTRY_AUTH_TOKEN: 'test_token',
SENTRY_ORG: 'test_org',
SENTRY_PROJECT: 'test_project',
},
}
);
const output = execSync(`node ${path.join(__dirname, '..', 'dist', 'index.js')}`, {
env: {
...process.env,
INPUT_ENVIRONMENT: 'production',
MOCK: 'true',
SENTRY_AUTH_TOKEN: 'test_token',
SENTRY_ORG: 'test_org',
SENTRY_PROJECT: 'test_project',
},
});
console.log(output.toString());
});
+1 -1
View File
@@ -148,7 +148,7 @@ runs:
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-avoid-sending-empty-urlPrefix
# For actions running on macos or windows runners, we use a composite
# action approach which allows us to install the arch specific sentry-cli
+14 -16
View File
@@ -122771,17 +122771,19 @@ const telemetry_1 = __nccwpck_require__(12417);
}
yield (0, telemetry_1.traceStep)('upload-sourcemaps', () => __awaiter(void 0, void 0, void 0, function* () {
core.debug(`Adding sourcemaps`);
const sourceMapsOptions = {
include: sourcemaps,
dist,
stripCommonPrefix,
};
// only set the urlPrefix if it's not empty
if (urlPrefix) {
sourceMapsOptions.urlPrefix = urlPrefix;
}
// sentry-cli supports multiple projects, but only uploads sourcemaps for the
// first project so we need to upload sourcemaps for each project individually
yield Promise.all(projects.map((project) => __awaiter(void 0, void 0, void 0, function* () {
// upload source maps can only do one project at a time
const localProjects = [project];
const sourceMapOptions = {
include: sourcemaps,
projects: localProjects,
dist,
urlPrefix,
stripCommonPrefix,
};
return (0, cli_1.getCLI)().uploadSourceMaps(release, sourceMapOptions);
return (0, cli_1.getCLI)().uploadSourceMaps(release, Object.assign(Object.assign({}, sourceMapsOptions), { projects: [project] }));
})));
Sentry.setTag('sourcemaps-uploaded', true);
}));
@@ -123002,11 +123004,7 @@ exports.getSetCommitsOption = getSetCommitsOption;
const checkEnvironmentVariables = () => {
if (process.env['MOCK']) {
// Set environment variables for mock runs if they aren't already
for (const variable of [
'SENTRY_AUTH_TOKEN',
'SENTRY_ORG',
'SENTRY_PROJECT',
]) {
for (const variable of ['SENTRY_AUTH_TOKEN', 'SENTRY_ORG', 'SENTRY_PROJECT']) {
if (!(variable in process.env)) {
process.env[variable] = variable;
}
@@ -123184,7 +123182,7 @@ exports.isSelfHosted = isSelfHosted;
* Determine if telemetry should be enabled.
*/
function isTelemetryEnabled() {
return (!ciOptions.getBooleanOption('disable_telemetry', false) && !isSelfHosted());
return !ciOptions.getBooleanOption('disable_telemetry', false) && !isSelfHosted();
}
exports.isTelemetryEnabled = isTelemetryEnabled;
+3 -3
View File
@@ -1,7 +1,7 @@
import {getTraceData} from '@sentry/node';
import SentryCli, {SentryCliReleases} from '@sentry/cli';
import { getTraceData } from '@sentry/node';
import SentryCli, { SentryCliReleases } from '@sentry/cli';
// @ts-ignore
import {version} from '../package.json';
import { version } from '../package.json';
/**
* CLI Singleton
+26 -24
View File
@@ -1,9 +1,10 @@
import * as Sentry from '@sentry/node';
import * as core from '@actions/core';
import {getCLI} from './cli';
import { SentryCliUploadSourceMapsOptions } from '@sentry/cli';
import { getCLI } from './cli';
import * as options from './options';
import * as process from 'process';
import {isTelemetryEnabled, traceStep, withTelemetry} from './telemetry';
import { isTelemetryEnabled, traceStep, withTelemetry } from './telemetry';
withTelemetry(
{
@@ -32,10 +33,7 @@ withTelemetry(
const setCommitsOption = options.getSetCommitsOption();
const projects = options.getProjects();
const urlPrefix = options.getUrlPrefixOption();
const stripCommonPrefix = options.getBooleanOption(
'strip_common_prefix',
false
);
const stripCommonPrefix = options.getBooleanOption('strip_common_prefix', false);
const release = await options.getRelease();
if (projects.length === 1) {
@@ -45,7 +43,7 @@ withTelemetry(
}
core.debug(`Release version is ${release}`);
await getCLI().new(release, {projects});
await getCLI().new(release, { projects });
Sentry.setTag('set-commits', setCommitsOption);
@@ -68,28 +66,32 @@ withTelemetry(
await traceStep('inject-debug-ids', async () => {
core.debug(`Injecting Debug IDs`);
// Unfortunately, @sentry/cli does not yet have an alias for inject
await getCLI().execute(
['sourcemaps', 'inject', ...sourcemaps],
true
);
await getCLI().execute(['sourcemaps', 'inject', ...sourcemaps], true);
});
}
await traceStep('upload-sourcemaps', async () => {
core.debug(`Adding sourcemaps`);
const sourceMapsOptions: SentryCliUploadSourceMapsOptions = {
include: sourcemaps,
dist,
stripCommonPrefix,
};
// only set the urlPrefix if it's not empty
if (urlPrefix) {
sourceMapsOptions.urlPrefix = urlPrefix;
}
// sentry-cli supports multiple projects, but only uploads sourcemaps for the
// first project so we need to upload sourcemaps for each project individually
await Promise.all(
projects.map(async project => {
// upload source maps can only do one project at a time
const localProjects: [string] = [project];
const sourceMapOptions = {
include: sourcemaps,
projects: localProjects,
dist,
urlPrefix,
stripCommonPrefix,
};
return getCLI().uploadSourceMaps(release, sourceMapOptions);
})
projects.map(async (project: string) =>
getCLI().uploadSourceMaps(release, {
...sourceMapsOptions,
projects: [project],
} as SentryCliUploadSourceMapsOptions & { projects: string[] })
)
);
Sentry.setTag('sourcemaps-uploaded', true);
@@ -101,7 +103,7 @@ withTelemetry(
core.debug(`Adding deploy to release`);
await getCLI().newDeploy(release, {
env: environment,
...(deployStartedAtOption && {started: deployStartedAtOption}),
...(deployStartedAtOption && { started: deployStartedAtOption }),
});
});
}
+7 -23
View File
@@ -1,6 +1,6 @@
import * as core from '@actions/core';
import path from 'path';
import {getCLI} from './cli';
import { getCLI } from './cli';
/**
* Get the release version string from parameter or propose one.
@@ -69,9 +69,7 @@ export const getStartedAt = (): number | null => {
}
if (!outputTimestamp || outputTimestamp < 0) {
throw new Error(
'started_at not in valid format. Unix timestamp or ISO 8601 date expected'
);
throw new Error('started_at not in valid format. Unix timestamp or ISO 8601 date expected');
}
return outputTimestamp;
@@ -109,10 +107,7 @@ export const getDist = (): string | undefined => {
* @param defaultValue boolean
* @returns boolean
*/
export const getBooleanOption = (
input: string,
defaultValue: boolean
): boolean => {
export const getBooleanOption = (input: string, defaultValue: boolean): boolean => {
const option = core.getInput(input);
if (!option) {
return defaultValue;
@@ -156,11 +151,7 @@ export const getSetCommitsOption = (): 'auto' | 'skip' => {
export const checkEnvironmentVariables = (): void => {
if (process.env['MOCK']) {
// Set environment variables for mock runs if they aren't already
for (const variable of [
'SENTRY_AUTH_TOKEN',
'SENTRY_ORG',
'SENTRY_PROJECT',
]) {
for (const variable of ['SENTRY_AUTH_TOKEN', 'SENTRY_ORG', 'SENTRY_PROJECT']) {
if (!(variable in process.env)) {
process.env[variable] = variable;
}
@@ -168,14 +159,10 @@ export const checkEnvironmentVariables = (): void => {
}
if (!process.env['SENTRY_ORG']) {
throw Error(
'Environment variable SENTRY_ORG is missing an organization slug'
);
throw Error('Environment variable SENTRY_ORG is missing an organization slug');
}
if (!process.env['SENTRY_AUTH_TOKEN']) {
throw Error(
'Environment variable SENTRY_AUTH_TOKEN is missing an auth token'
);
throw Error('Environment variable SENTRY_AUTH_TOKEN is missing an auth token');
}
};
@@ -205,8 +192,5 @@ export const getWorkingDirectory = (): string => {
// The action runs inside `github.action_path` and as such
// doesn't automatically have access to the user's git
// We prefix all paths with `GITHUB_WORKSPACE` which is at the top of the repo.
return path.join(
process.env.GITHUB_WORKSPACE || '',
core.getInput('working_directory')
);
return path.join(process.env.GITHUB_WORKSPACE || '', core.getInput('working_directory'));
};
+5 -12
View File
@@ -8,10 +8,7 @@ const SENTRY_SAAS_HOSTNAME = 'sentry.io';
* Initializes Sentry and wraps the given callback
* in a span.
*/
export async function withTelemetry<F>(
options: {enabled: boolean},
callback: () => F | Promise<F>
): Promise<F> {
export async function withTelemetry<F>(options: { enabled: boolean }, callback: () => F | Promise<F>): Promise<F> {
Sentry.initWithoutDefaultIntegrations({
dsn: 'https://[email protected]/4508608809533441',
enabled: options.enabled,
@@ -27,7 +24,7 @@ export async function withTelemetry<F>(
const org = process.env['SENTRY_ORG'];
Sentry.setUser({id: org});
Sentry.setUser({ id: org });
Sentry.setTag('organization', org);
Sentry.setTag('node', process.version);
Sentry.setTag('platform', process.platform);
@@ -68,7 +65,7 @@ export function updateProgress(step: string): void {
*/
export function traceStep<T>(step: string, callback: () => T): T {
updateProgress(step);
return Sentry.startSpan({name: step, op: 'action.step'}, () => callback());
return Sentry.startSpan({ name: step, op: 'action.step' }, () => callback());
}
/**
@@ -87,9 +84,7 @@ export async function safeFlush(): Promise<void> {
* Check whether the user is self-hosting Sentry.
*/
export function isSelfHosted(): boolean {
const url = new URL(
process.env['SENTRY_URL'] || `https://${SENTRY_SAAS_HOSTNAME}`
);
const url = new URL(process.env['SENTRY_URL'] || `https://${SENTRY_SAAS_HOSTNAME}`);
return url.hostname !== SENTRY_SAAS_HOSTNAME;
}
@@ -98,7 +93,5 @@ export function isSelfHosted(): boolean {
* Determine if telemetry should be enabled.
*/
export function isTelemetryEnabled(): boolean {
return (
!ciOptions.getBooleanOption('disable_telemetry', false) && !isSelfHosted()
);
return !ciOptions.getBooleanOption('disable_telemetry', false) && !isSelfHosted();
}