Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,58 @@ jobs:
repo: getsentry/action-release
commit: ${{ github.sha }}
previous_commit: ${{ github.sha }}

test-upload-debug-files:
needs: docker-build
strategy:
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
name: Test debug files upload
permissions:
contents: read
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0

# Breakpad symbol files are plain text, so we can create one on every OS
# without a compiler. A random debug ID per run makes sure we find this
# upload and not one from an earlier run.
- name: Create a Breakpad debug file
id: dif
shell: bash
run: |
HEX=$(od -An -N16 -tx1 /dev/urandom | tr -d ' \n' | tr 'a-f' 'A-F')
mkdir -p e2e-debug-files
printf 'MODULE Linux x86_64 %s0 action-release-e2e\nFILE 0 main.c\nFUNC 1000 10 0 main\n1000 10 1 0\n' "$HEX" > e2e-debug-files/action-release-e2e.sym
DEBUG_ID=$(echo "$HEX" | tr 'A-F' 'a-f' | sed -E 's/(.{8})(.{4})(.{4})(.{4})(.{12})/\1-\2-\3-\4-\5/')
echo "debug_id=$DEBUG_ID" >> $GITHUB_OUTPUT

- name: Create a release and upload debug files
uses: ./
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_LOG_LEVEL: debug
with:
ignore_missing: true
debug_files: ./e2e-debug-files

- name: Check that Sentry has the debug file
shell: bash
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
DEBUG_ID: ${{ steps.dif.outputs.debug_id }}
run: |
for i in 1 2 3 4 5; do
# Use the jq exit code, not its output: jq on Windows writes CRLF.
if curl -sf -H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
"https://sentry.io/api/0/projects/$SENTRY_ORG/$SENTRY_PROJECT/files/dsyms/?query=$DEBUG_ID" \
| jq -e --arg id "$DEBUG_ID" 'any(.[]; .debugId == $id)' > /dev/null; then
echo "SUCCESS: Found debug file $DEBUG_ID"
exit 0
fi
sleep 5
done
echo "ERROR: Debug file $DEBUG_ID not found in Sentry"
exit 1
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Adding the following to your workflow will create a new Sentry release and tell
| `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` |
| `debug_files` | Space-separated list of paths to debug information files (e.g. dSYM, PDB, ELF, Breakpad). Paths cannot contain spaces. Omit to skip uploading debug files. | - |
| `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` |
Expand Down Expand Up @@ -103,6 +104,15 @@ Adding the following to your workflow will create a new Sentry release and tell
sourcemaps: './dist'
```

- Create a new Sentry release and upload debug information files for a native app.

```yaml
- uses: getsentry/action-release@v3
with:
environment: 'production'
debug_files: './build/app.pdb ./build/app.dSYM'
```

- Create a new Sentry release for the `production` environment of your project at version `v1.0.1`.

```yaml
Expand Down
16 changes: 16 additions & 0 deletions __tests__/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
getBooleanOption,
getDist,
getSourcemaps,
getDebugFiles,
getStartedAt,
getRelease,
getSetCommitsOption,
Expand Down Expand Up @@ -68,6 +69,21 @@ describe('options', () => {
});
});

describe('getDebugFiles', () => {
afterEach(() => {
delete process.env['INPUT_DEBUG_FILES'];
});

test('should return empty list when debug_files is omitted', () => {
expect(getDebugFiles()).toEqual([]);
});

test('should split whitespace-separated paths and drop empty entries', () => {
process.env['INPUT_DEBUG_FILES'] = ' ./build/app.pdb ./build/app.dSYM\n';
expect(getDebugFiles()).toEqual(['./build/app.pdb', './build/app.dSYM']);
});
});

describe('getDist', () => {
afterEach(() => {
delete process.env['INPUT_DIST'];
Expand Down
10 changes: 9 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ inputs:
Does nothing if "sourcemaps" was not set.
default: true
required: false
debug_files:
description: |-
Space-separated list of paths to debug information files (e.g. dSYM, PDB,
ELF, Breakpad). Uploaded with "sentry-cli debug-files upload".
Paths cannot contain spaces. Omit to skip uploading debug files.
required: false
dist:
description: |-
Unique identifier for the distribution, used to further segment your
Expand Down Expand Up @@ -148,6 +154,7 @@ runs:
INPUT_ENVIRONMENT: ${{ inputs.environment }}
INPUT_INJECT: ${{ inputs.inject }}
INPUT_SOURCEMAPS: ${{ inputs.sourcemaps }}
INPUT_DEBUG_FILES: ${{ inputs.debug_files }}
INPUT_DIST: ${{ inputs.dist }}
INPUT_FINALIZE: ${{ inputs.finalize }}
INPUT_IGNORE_MISSING: ${{ inputs.ignore_missing }}
Expand All @@ -167,7 +174,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:ab-migrate-to-vitest
uses: docker://ghcr.io/getsentry/action-release-image:ab-upload-debug-files

# For actions running on macos or windows runners, we use a composite
# action approach which allows us to install the arch specific sentry-cli
Expand Down Expand Up @@ -214,6 +221,7 @@ runs:
INPUT_ENVIRONMENT: ${{ inputs.environment }}
INPUT_INJECT: ${{ inputs.inject }}
INPUT_SOURCEMAPS: ${{ inputs.sourcemaps }}
INPUT_DEBUG_FILES: ${{ inputs.debug_files }}
INPUT_DIST: ${{ inputs.dist }}
INPUT_FINALIZE: ${{ inputs.finalize }}
INPUT_IGNORE_MISSING: ${{ inputs.ignore_missing }}
Expand Down
25 changes: 24 additions & 1 deletion dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -99836,6 +99836,7 @@ const telemetry_1 = __nccwpck_require__(12417);
const environment = options.getEnvironment();
const inject = options.getBooleanOption('inject', true);
const sourcemaps = options.getSourcemaps();
const debugFiles = options.getDebugFiles();
const dist = options.getDist();
const shouldFinalize = options.getBooleanOption('finalize', true);
const ignoreMissing = options.getBooleanOption('ignore_missing', false);
Expand Down Expand Up @@ -99904,6 +99905,16 @@ const telemetry_1 = __nccwpck_require__(12417);
Sentry.setTag('sourcemaps-uploaded', true);
}));
}
Sentry.setTag('debug-files', debugFiles.length > 0);
if (debugFiles.length) {
yield (0, telemetry_1.traceStep)('upload-debug-files', () => __awaiter(void 0, void 0, void 0, function* () {
core.debug(`Uploading debug files`);
// Debug files belong to a project, not a release, so upload them for each project
for (const project of projects) {
yield (0, cli_1.getCLI)().execute(['debug-files', 'upload', '--project', project, ...debugFiles], true);
}
}));
}
if (environment) {
yield (0, telemetry_1.traceStep)('add-environment', () => __awaiter(void 0, void 0, void 0, function* () {
core.debug(`Adding deploy to release`);
Expand Down Expand Up @@ -99976,7 +99987,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.getSetCommitsManualOptions = 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.getDebugFiles = 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);
Expand Down Expand Up @@ -100061,6 +100072,18 @@ const getSourcemaps = () => {
return sourcemapsOption.split(' ');
};
exports.getSourcemaps = getSourcemaps;
/**
* Debug files are optional, but there may be several as a whitespace-separated list.
* Paths cannot contain spaces.
* @returns string[]
*/
const getDebugFiles = () => {
return core
.getInput('debug_files')
.split(/\s+/)
.filter(file => file.length > 0);
};
exports.getDebugFiles = getDebugFiles;
/**
* Dist is optional, but should be a string when provided.
* @returns string
Expand Down
13 changes: 13 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ withTelemetry(
const environment = options.getEnvironment();
const inject = options.getBooleanOption('inject', true);
const sourcemaps = options.getSourcemaps();
const debugFiles = options.getDebugFiles();
const dist = options.getDist();
const shouldFinalize = options.getBooleanOption('finalize', true);
const ignoreMissing = options.getBooleanOption('ignore_missing', false);
Expand Down Expand Up @@ -114,6 +115,18 @@ withTelemetry(
});
}

Sentry.setTag('debug-files', debugFiles.length > 0);

if (debugFiles.length) {
await traceStep('upload-debug-files', async () => {
core.debug(`Uploading debug files`);
// Debug files belong to a project, not a release, so upload them for each project
for (const project of projects) {
await getCLI().execute(['debug-files', 'upload', '--project', project, ...debugFiles], true);
}
});
}

if (environment) {
await traceStep('add-environment', async () => {
core.debug(`Adding deploy to release`);
Expand Down
12 changes: 12 additions & 0 deletions src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,18 @@ export const getSourcemaps = (): string[] => {
return sourcemapsOption.split(' ');
};

/**
* Debug files are optional, but there may be several as a whitespace-separated list.
* Paths cannot contain spaces.
* @returns string[]
*/
export const getDebugFiles = (): string[] => {
return core
.getInput('debug_files')
.split(/\s+/)
.filter(file => file.length > 0);
};

/**
* Dist is optional, but should be a string when provided.
* @returns string
Expand Down
Loading