Skip to content

Commit bad5e2d

Browse files
Merge pull request #13 from MindscapeHQ/feature/7-deployments
feat: #7 - Deployment tracking
2 parents e43ed29 + ad72134 commit bad5e2d

File tree

7 files changed

+253
-4
lines changed

7 files changed

+253
-4
lines changed

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,41 @@ Provide the identifier (`id`) of the symbols file (e.g. `--id=2c7a3u3`). You can
154154
raygun-cli symbols delete --id=<id> --app-id=APP_ID --token=TOKEN
155155
```
156156

157+
#### Deployment Tracking
158+
159+
Send deployment tracking notifications to [raygun.com](https://raygun.com).
160+
161+
Documentation: https://raygun.com/documentation/product-guides/deployment-tracking/overview/
162+
163+
Minimal arguments are:
164+
165+
```
166+
raygun-cli depoyments --app-id=APP_ID --token=TOKEN --version=<app version> --api-key=<Raygun app API key>
167+
```
168+
169+
Example outputs:
170+
171+
```
172+
Success:
173+
174+
Success creating deployment: 201
175+
Deployment identifier: 2cewu0m
176+
Deployment created successfully
177+
178+
Missing Access Token:
179+
180+
Error creating deployment: 401
181+
Response: {"type":"https://tools.ietf.org/html/rfc9110#section-15.5.2","title":"Unauthorized","status":401,"traceId":"00-b9f01ba3ff4a938501c760e6924acc81-53e9411804aa9f2f-00"}
182+
Failed to create deployment
183+
184+
Access Token misses access to application:
185+
186+
Error creating deployment: 404
187+
Response: {"type":"https://tools.ietf.org/html/rfc9110#section-15.5.5","title":"Not Found","status":404,"traceId":"00-5c3a2423f922d787e20d01456b6c1836-b88c29232af94db8-00"}
188+
Failed to create deployment
189+
190+
```
191+
157192
## Development
158193

159194
### Compiling a binary

bin/raygun_cli.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:args/args.dart';
22
import 'package:raygun_cli/sourcemap/sourcemap_command.dart';
33
import 'package:raygun_cli/symbols/flutter_symbols.dart';
4+
import 'package:raygun_cli/deployments/deployments_command.dart';
45

56
const String version = '0.0.2';
67

@@ -30,6 +31,10 @@ ArgParser buildParser() {
3031
..addCommand(
3132
kSymbolsCommand,
3233
buildParserSymbols(),
34+
)
35+
..addCommand(
36+
kDeploymentsCommand,
37+
buildParserDeployments(),
3338
);
3439
}
3540

@@ -74,6 +79,11 @@ void main(List<String> arguments) {
7479
return;
7580
}
7681

82+
if (results.command?.name == kDeploymentsCommand) {
83+
parseDeploymentsCommand(results.command!, verbose);
84+
return;
85+
}
86+
7787
if (verbose) {
7888
print('[VERBOSE] All arguments: ${results.arguments}');
7989
}

lib/deployments/deployments.dart

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import 'dart:io';
2+
3+
import 'package:raygun_cli/deployments/deployments_api.dart';
4+
import 'package:args/args.dart';
5+
import '../config_props.dart';
6+
7+
class Deployments {
8+
final ArgResults command;
9+
final bool verbose;
10+
late final String appId;
11+
late final String token;
12+
13+
Deployments({
14+
required this.command,
15+
required this.verbose,
16+
required ConfigProps config,
17+
}) {
18+
appId = config.appId;
19+
token = config.token;
20+
}
21+
22+
Future<void> notify() async {
23+
if (!command.wasParsed('api-key')) {
24+
print('Error: Missing "--api-key"');
25+
print(' Please provide "--api-key" via argument');
26+
exit(2);
27+
}
28+
if (!command.wasParsed('version')) {
29+
print('Error: Missing "--version"');
30+
print(' Please provide "--version" via argument');
31+
exit(2);
32+
}
33+
34+
final apiKey = command.option('api-key') as String;
35+
final version = command.option('version') as String;
36+
final ownerName = command.option('owner-name');
37+
final emailAddress = command.option('email-address');
38+
final comment = command.option('comment');
39+
final scmIdentifier = command.option('scm-identifier');
40+
final scmType = command.option('scm-type');
41+
42+
if (verbose) {
43+
print('app-id: $appId');
44+
print('token: $token');
45+
print('api-key: $apiKey');
46+
print('version: $version');
47+
print('owner-name: $ownerName');
48+
print('email-address: $emailAddress');
49+
print('comment: $comment');
50+
print('scm-identifier: $scmIdentifier');
51+
print('scm-type: $scmType');
52+
}
53+
54+
final success = await createDeployment(
55+
token: token,
56+
apiKey: apiKey,
57+
version: version,
58+
ownerName: ownerName,
59+
emailAddress: emailAddress,
60+
comment: comment,
61+
scmIdentifier: scmIdentifier,
62+
scmType: scmType,
63+
);
64+
65+
if (success) {
66+
print('Deployment created successfully');
67+
exit(0);
68+
} else {
69+
print('Failed to create deployment');
70+
exit(2);
71+
}
72+
}
73+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import 'dart:convert';
2+
import 'package:http/http.dart' as http;
3+
4+
Future<bool> createDeployment({
5+
required String token,
6+
required String apiKey,
7+
required String version,
8+
String? ownerName,
9+
String? emailAddress,
10+
String? comment,
11+
String? scmIdentifier,
12+
String? scmType,
13+
}) async {
14+
final url =
15+
'https://api.raygun.com/v3/applications/api-key/$apiKey/deployments';
16+
17+
final payload = {
18+
'version': version,
19+
if (ownerName != null) 'ownerName': ownerName,
20+
if (emailAddress != null) 'emailAddress': emailAddress,
21+
if (comment != null) 'comment': comment,
22+
if (scmIdentifier != null) 'scmIdentifier': scmIdentifier,
23+
if (scmType != null) 'scmType': scmType,
24+
'deployedAt': DateTime.now().toUtc().toIso8601String(),
25+
};
26+
27+
try {
28+
final response = await http.post(
29+
Uri.parse(url),
30+
headers: {
31+
'Authorization': 'Bearer $token',
32+
'Content-Type': 'application/json',
33+
},
34+
body: jsonEncode(payload),
35+
);
36+
37+
if (response.statusCode == 200 || response.statusCode == 201) {
38+
print('Success creating deployment: ${response.statusCode}');
39+
print(
40+
'Deployment identifier: ${jsonDecode(response.body)['identifier']}');
41+
return true;
42+
}
43+
44+
print('Error creating deployment: ${response.statusCode}');
45+
print('Response: ${response.body}');
46+
return false;
47+
} catch (e) {
48+
print('Exception while creating deployment: $e');
49+
return false;
50+
}
51+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import 'dart:io';
2+
3+
import 'package:args/args.dart';
4+
import 'package:raygun_cli/deployments/deployments.dart';
5+
6+
import '../config_props.dart';
7+
8+
const kDeploymentsCommand = 'deployments';
9+
10+
ArgParser buildParserDeployments() {
11+
return ArgParser()
12+
..addFlag(
13+
'help',
14+
abbr: 'h',
15+
negatable: false,
16+
help: 'Print deployments usage information',
17+
)
18+
..addOption(
19+
'app-id',
20+
help: 'Raygun application ID',
21+
)
22+
..addOption(
23+
'token',
24+
help: 'Raygun access token',
25+
)
26+
..addOption(
27+
'api-key',
28+
mandatory: true,
29+
help: 'API key from the Raygun account you are deploying to',
30+
)
31+
..addOption(
32+
'version',
33+
mandatory: true,
34+
help:
35+
'Version of the software you are deploying and want Raygun to know about',
36+
)
37+
..addOption(
38+
'scm-type',
39+
mandatory: false,
40+
allowed: ['GitHub', 'Bitbucket', 'GitLab', 'AzureDevOps'],
41+
help:
42+
'Type of the source control management system you are deploying from - if provided, one of [GitHub, Bitbucket, GitLab, AzureDevOps]',
43+
)
44+
..addOption(
45+
'scm-identifier',
46+
mandatory: false,
47+
help: 'Commit that this deployment is based on',
48+
)
49+
..addOption(
50+
'owner-name',
51+
mandatory: false,
52+
help: 'Name of the person deploying the software',
53+
)
54+
..addOption(
55+
'email-address',
56+
mandatory: false,
57+
help: 'Email address of the person deploying the software',
58+
)
59+
..addOption(
60+
'comment',
61+
mandatory: false,
62+
help: 'Deployment comment',
63+
);
64+
}
65+
66+
void parseDeploymentsCommand(ArgResults command, bool verbose) {
67+
if (command.wasParsed('help')) {
68+
print('Usage: raygun-cli deployments <arguments>');
69+
print(buildParserDeployments().usage);
70+
exit(0);
71+
}
72+
73+
final configProps = ConfigProps.load(command, verbose: verbose);
74+
75+
Deployments(
76+
command: command,
77+
verbose: verbose,
78+
config: configProps,
79+
).notify();
80+
}

lib/sourcemap/sourcemap_command.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ ArgParser buildParserSourcemap() {
1919
)
2020
..addOption(
2121
'app-id',
22-
help: 'Raygun\'s application ID',
22+
help: 'Raygun application ID',
2323
)
2424
..addOption(
2525
'token',
26-
help: 'Raygun\'s access token',
26+
help: 'Raygun access token',
2727
)
2828
..addOption(
2929
'platform',

lib/symbols/flutter_symbols.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@ ArgParser buildParserSymbols() {
8484
)
8585
..addOption(
8686
'app-id',
87-
help: 'Raygun\'s application ID',
87+
help: 'Raygun application ID',
8888
)
8989
..addOption(
9090
'token',
91-
help: 'Raygun\'s access token',
91+
help: 'Raygun access token',
9292
)
9393
..addOption(
9494
'path',

0 commit comments

Comments
 (0)