Merge pull request #5 from getsentry/feat/validate

This commit is contained in:
Marcos Gaeta
2020-07-14 13:12:36 -07:00
committed by GitHub
6 changed files with 116 additions and 35 deletions
+28 -10
View File
@@ -1,6 +1,9 @@
# Sentry Release GitHub Action
A release is a version of your code that is deployed to an environment. When you give Sentry information about your releases, you unlock a number of new features:
Automatically create a Sentry Release in a workflow.
## Why Releases?
A release is a version of your code that is deployed to an environment.
When you give Sentry information about your releases, you unlock a number of new features:
- Determine the issues and regressions introduced in a new release
- Predict which commit caused an issue and who is likely responsible
- Resolve issues by including the issue number in your commit message
@@ -9,19 +12,34 @@ A release is a version of your code that is deployed to an environment. When you
Additionally, releases are used for applying source maps to minified JavaScript to view original, untransformed source code.
## Usage
Adding the following to your workflow will create a new Sentry release for the
`production` environment of your project and upload JavaScript source maps from
the `./lib` directory.
```yaml
steps:
...
- name: Sentry Release
uses: getsentry/[email protected]
with:
environment: 'production'
sourcemaps: './lib'
uses: getsentry/action-release@v1
with:
environment: 'production'
sourcemaps: './lib'
```
## Troubleshooting
Don’t forget to include the following environment variables:
```yaml
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
```
See [action.yml](https://github.com/getsentry/action-release/blob/master/action.yml) for more workflow options.
## Troubleshooting
Building and running this action locally on an unsupported environment yields an error that looks like:
```
Syntax error: end of file unexpected (expecting ")")
```
Suggestions and issues can be posted on the repository's [issues page](https://github.com/getsentry/action-release/issues).
## Contributing
See the [Contributing Guide](https://github.com/getsentry/action-release/blob/master/CONTRIBUTING).
+54 -4
View File
@@ -1,19 +1,18 @@
import {execSync} from 'child_process';
import * as path from 'path';
import * as process from 'process';
import {getShouldFinalize} from '../src/validate';
import {getShouldFinalize, getSourcemaps, getStartedAt} from '../src/validate'
describe('validate', () => {
describe('getShouldFinalize', () => {
const errorMessage = 'finalize is not a boolean';
afterEach(() => {
delete process.env['INPUT_FINALIZE'];
});
test('should throw an error when finalize is invalid', async () => {
process.env['INPUT_FINALIZE'] = 'error';
expect(() => getShouldFinalize()).toThrow(
'finalize is not a boolean'
);
expect(() => getShouldFinalize()).toThrow(errorMessage);
});
test('should return true when finalize is omitted', async () => {
@@ -25,6 +24,57 @@ describe('validate', () => {
expect(getShouldFinalize()).toBe(false);
});
});
describe('getSourcemaps', () => {
afterEach(() => {
delete process.env['INPUT_SOURCEMAPS'];
});
test('should return empty list when sourcemaps is omitted', async () => {
expect(getSourcemaps()).toEqual([]);
});
test('should return array when sourcemaps is false', () => {
process.env['INPUT_SOURCEMAPS'] = './lib';
expect(getSourcemaps()).toEqual(['./lib']);
});
});
describe('getStartedAt', () => {
const errorMessage = 'started_at not in valid format. Unix timestamp or ISO 8601 date expected';
afterEach(() => {
delete process.env['INPUT_STARTED_AT'];
});
test('should throw an error when started_at is negative', async () => {
process.env['INPUT_STARTED_AT'] = '-1';
expect(() => getStartedAt()).toThrow(errorMessage);
});
test('should throw an error when started_at is invalid', async () => {
process.env['INPUT_STARTED_AT'] = 'error';
expect(() => getStartedAt()).toThrow(errorMessage);
});
test('should return null when started_at is omitted', async () => {
expect(getStartedAt()).toBeNull();
});
test('should return an integer when started_at is an ISO8601 string.', async () => {
process.env['INPUT_STARTED_AT'] = '2017-07-13T19:40:00';
expect(getStartedAt()).toEqual(1500000000);
});
test('should return an integer when started_at is an truncated ISO8601 string.', async () => {
process.env['INPUT_STARTED_AT'] = '2017-07-13';
expect(getStartedAt()).toEqual(1499904000);
});
test('should return an integer when started_at is an integer.', async () => {
process.env['INPUT_STARTED_AT'] = '1500000000';
expect(getStartedAt()).toEqual(1500000000);
});
});
});
// shows how the runner will run a javascript action with env / stdout protocol
+4 -4
View File
@@ -3,16 +3,16 @@ description: 'GitHub Action for creating a release on Sentry'
author: 'Sentry'
inputs:
environment:
description: 'The name of the environment. E.g. production. TODO'
description: 'Set the environment for this release. E.g. "production" or "staging".'
required: true
sourcemaps:
description: 'Space-separated list of paths to JavaScript sourcemaps. Omit to skip _sending_ sourcemaps.'
description: 'Space-separated list of paths to JavaScript sourcemaps. Omit to skip uploading sourcemaps.'
required: false
finalize:
description: 'When false, omit setting the _TODO_ timestamp on the release.'
description: 'When false, omit marking the release as finalized and released.'
default: true
started_at:
description: 'Unix timestamp '
description: 'Unix timestamp of the release start date.'
required: false
runs:
using: 'docker'
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "action-release",
"version": "0.0.1",
"version": "1.0.0",
"private": true,
"description": "GitHub Action for creating a release on Sentry",
"main": "dist/index.js",
+7 -5
View File
@@ -4,6 +4,8 @@ import * as validate from './validate';
(async () => {
try {
const cli = getCLI();
// Validate parameters first so we can fail early.
validate.checkEnvironmentVariables();
const environment = validate.getEnvironment();
@@ -14,25 +16,25 @@ import * as validate from './validate';
const version = await validate.getVersion();
core.debug(`Version is ${version}`);
await getCLI().new(version);
await cli.new(version);
core.debug(`Setting commits`);
await getCLI().setCommits(version, {auto: true});
await cli.setCommits(version, {auto: true});
if (sourcemaps) {
core.debug(`Adding sourcemaps`);
await getCLI().uploadSourceMaps(version, {include: sourcemaps});
await cli.uploadSourceMaps(version, {include: sourcemaps});
}
core.debug(`Adding deploy to release`);
await getCLI().newDeploy(version, {
await cli.newDeploy(version, {
env: environment,
...(deployStartedAtOption && {started: deployStartedAtOption}),
});
core.debug(`Finalizing the release`);
if (shouldFinalize) {
await getCLI().finalize(version);
await cli.finalize(version);
}
core.debug(`Done`);
+22 -11
View File
@@ -17,7 +17,7 @@ export const getVersion = async (): Promise<string> => {
};
/**
* Environment is required.
* Get `environment`, a required parameter.
* @throws
* @returns string
*/
@@ -26,10 +26,9 @@ export const getEnvironment = (): string => {
};
/**
* TODO I don't want to duplicate logic here. How should I validate the timestamp?
* TODO must be a UNIX timestamp
* TODO what if this is in the future?
* TODO FIRST it could also be a datetime.
* Optionally get a UNIX timestamp of when the deployment started.
* Input timestamp may also be ISO 8601.
*
* @throws
* @returns number
*/
@@ -39,16 +38,28 @@ export const getStartedAt = (): number | null => {
return null;
}
const startedAt = parseInt(startedAtOption);
if (isNaN(startedAt)) {
throw new Error('started_at is not a number');
// In sentry-cli, we parse integer first.
const startedAtTimestamp = parseInt(startedAtOption);
const startedAt8601 = Math.floor(Date.parse(startedAtOption) / 1000);
let outputTimestamp;
if (!isNaN(startedAtTimestamp)) {
outputTimestamp = startedAtTimestamp;
} else if (!isNaN(startedAt8601)) {
outputTimestamp = startedAt8601;
}
return startedAt;
if (!outputTimestamp || outputTimestamp < 0) {
throw new Error(
'started_at not in valid format. Unix timestamp or ISO 8601 date expected'
);
}
return outputTimestamp;
};
/**
* TODO EXPLAIN FORMAT IN THE README
* TODO handle failure
* Source maps are optional, but there may be several as a space-separated list.
* @returns string[]
*/
export const getSourcemaps = (): string[] => {