diff --git a/README.md b/README.md index 1f3ffdc..c13b695 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,41 @@ Provide the identifier (`id`) of the symbols file (e.g. `--id=2c7a3u3`). You can raygun-cli symbols delete --id= --app-id=APP_ID --token=TOKEN ``` +#### Deployment Tracking + +Send deployment tracking notifications to [raygun.com](https://raygun.com). + +Documentation: https://raygun.com/documentation/product-guides/deployment-tracking/overview/ + +Minimal arguments are: + +``` +raygun-cli depoyments --app-id=APP_ID --token=TOKEN --version= --api-key= +``` + +Example outputs: + +``` +Success: + +Success creating deployment: 201 +Deployment identifier: 2cewu0m +Deployment created successfully + +Missing Access Token: + +Error creating deployment: 401 +Response: {"type":"https://tools.ietf.org/html/rfc9110#section-15.5.2","title":"Unauthorized","status":401,"traceId":"00-b9f01ba3ff4a938501c760e6924acc81-53e9411804aa9f2f-00"} +Failed to create deployment + +Access Token misses access to application: + +Error creating deployment: 404 +Response: {"type":"https://tools.ietf.org/html/rfc9110#section-15.5.5","title":"Not Found","status":404,"traceId":"00-5c3a2423f922d787e20d01456b6c1836-b88c29232af94db8-00"} +Failed to create deployment + +``` + ## Development ### Compiling a binary diff --git a/bin/raygun_cli.dart b/bin/raygun_cli.dart index 74c6e7e..fa0d82d 100644 --- a/bin/raygun_cli.dart +++ b/bin/raygun_cli.dart @@ -1,6 +1,7 @@ import 'package:args/args.dart'; import 'package:raygun_cli/sourcemap/sourcemap_command.dart'; import 'package:raygun_cli/symbols/flutter_symbols.dart'; +import 'package:raygun_cli/deployments/deployments_command.dart'; const String version = '0.0.2'; @@ -30,6 +31,10 @@ ArgParser buildParser() { ..addCommand( kSymbolsCommand, buildParserSymbols(), + ) + ..addCommand( + kDeploymentsCommand, + buildParserDeployments(), ); } @@ -74,6 +79,11 @@ void main(List arguments) { return; } + if (results.command?.name == kDeploymentsCommand) { + parseDeploymentsCommand(results.command!, verbose); + return; + } + if (verbose) { print('[VERBOSE] All arguments: ${results.arguments}'); } diff --git a/lib/deployments/deployments.dart b/lib/deployments/deployments.dart new file mode 100644 index 0000000..924727a --- /dev/null +++ b/lib/deployments/deployments.dart @@ -0,0 +1,73 @@ +import 'dart:io'; + +import 'package:raygun_cli/deployments/deployments_api.dart'; +import 'package:args/args.dart'; +import '../config_props.dart'; + +class Deployments { + final ArgResults command; + final bool verbose; + late final String appId; + late final String token; + + Deployments({ + required this.command, + required this.verbose, + required ConfigProps config, + }) { + appId = config.appId; + token = config.token; + } + + Future notify() async { + if (!command.wasParsed('api-key')) { + print('Error: Missing "--api-key"'); + print(' Please provide "--api-key" via argument'); + exit(2); + } + if (!command.wasParsed('version')) { + print('Error: Missing "--version"'); + print(' Please provide "--version" via argument'); + exit(2); + } + + final apiKey = command.option('api-key') as String; + final version = command.option('version') as String; + final ownerName = command.option('owner-name'); + final emailAddress = command.option('email-address'); + final comment = command.option('comment'); + final scmIdentifier = command.option('scm-identifier'); + final scmType = command.option('scm-type'); + + if (verbose) { + print('app-id: $appId'); + print('token: $token'); + print('api-key: $apiKey'); + print('version: $version'); + print('owner-name: $ownerName'); + print('email-address: $emailAddress'); + print('comment: $comment'); + print('scm-identifier: $scmIdentifier'); + print('scm-type: $scmType'); + } + + final success = await createDeployment( + token: token, + apiKey: apiKey, + version: version, + ownerName: ownerName, + emailAddress: emailAddress, + comment: comment, + scmIdentifier: scmIdentifier, + scmType: scmType, + ); + + if (success) { + print('Deployment created successfully'); + exit(0); + } else { + print('Failed to create deployment'); + exit(2); + } + } +} diff --git a/lib/deployments/deployments_api.dart b/lib/deployments/deployments_api.dart new file mode 100644 index 0000000..cdfc4ae --- /dev/null +++ b/lib/deployments/deployments_api.dart @@ -0,0 +1,51 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; + +Future createDeployment({ + required String token, + required String apiKey, + required String version, + String? ownerName, + String? emailAddress, + String? comment, + String? scmIdentifier, + String? scmType, +}) async { + final url = + 'https://api.raygun.com/v3/applications/api-key/$apiKey/deployments'; + + final payload = { + 'version': version, + if (ownerName != null) 'ownerName': ownerName, + if (emailAddress != null) 'emailAddress': emailAddress, + if (comment != null) 'comment': comment, + if (scmIdentifier != null) 'scmIdentifier': scmIdentifier, + if (scmType != null) 'scmType': scmType, + 'deployedAt': DateTime.now().toUtc().toIso8601String(), + }; + + try { + final response = await http.post( + Uri.parse(url), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + body: jsonEncode(payload), + ); + + if (response.statusCode == 200 || response.statusCode == 201) { + print('Success creating deployment: ${response.statusCode}'); + print( + 'Deployment identifier: ${jsonDecode(response.body)['identifier']}'); + return true; + } + + print('Error creating deployment: ${response.statusCode}'); + print('Response: ${response.body}'); + return false; + } catch (e) { + print('Exception while creating deployment: $e'); + return false; + } +} diff --git a/lib/deployments/deployments_command.dart b/lib/deployments/deployments_command.dart new file mode 100644 index 0000000..7bf9948 --- /dev/null +++ b/lib/deployments/deployments_command.dart @@ -0,0 +1,80 @@ +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:raygun_cli/deployments/deployments.dart'; + +import '../config_props.dart'; + +const kDeploymentsCommand = 'deployments'; + +ArgParser buildParserDeployments() { + return ArgParser() + ..addFlag( + 'help', + abbr: 'h', + negatable: false, + help: 'Print deployments usage information', + ) + ..addOption( + 'app-id', + help: 'Raygun application ID', + ) + ..addOption( + 'token', + help: 'Raygun access token', + ) + ..addOption( + 'api-key', + mandatory: true, + help: 'API key from the Raygun account you are deploying to', + ) + ..addOption( + 'version', + mandatory: true, + help: + 'Version of the software you are deploying and want Raygun to know about', + ) + ..addOption( + 'scm-type', + mandatory: false, + allowed: ['GitHub', 'Bitbucket', 'GitLab', 'AzureDevOps'], + help: + 'Type of the source control management system you are deploying from - if provided, one of [GitHub, Bitbucket, GitLab, AzureDevOps]', + ) + ..addOption( + 'scm-identifier', + mandatory: false, + help: 'Commit that this deployment is based on', + ) + ..addOption( + 'owner-name', + mandatory: false, + help: 'Name of the person deploying the software', + ) + ..addOption( + 'email-address', + mandatory: false, + help: 'Email address of the person deploying the software', + ) + ..addOption( + 'comment', + mandatory: false, + help: 'Deployment comment', + ); +} + +void parseDeploymentsCommand(ArgResults command, bool verbose) { + if (command.wasParsed('help')) { + print('Usage: raygun-cli deployments '); + print(buildParserDeployments().usage); + exit(0); + } + + final configProps = ConfigProps.load(command, verbose: verbose); + + Deployments( + command: command, + verbose: verbose, + config: configProps, + ).notify(); +} diff --git a/lib/sourcemap/sourcemap_command.dart b/lib/sourcemap/sourcemap_command.dart index 3e60f74..f1cc9ff 100644 --- a/lib/sourcemap/sourcemap_command.dart +++ b/lib/sourcemap/sourcemap_command.dart @@ -19,11 +19,11 @@ ArgParser buildParserSourcemap() { ) ..addOption( 'app-id', - help: 'Raygun\'s application ID', + help: 'Raygun application ID', ) ..addOption( 'token', - help: 'Raygun\'s access token', + help: 'Raygun access token', ) ..addOption( 'platform', diff --git a/lib/symbols/flutter_symbols.dart b/lib/symbols/flutter_symbols.dart index 209189e..26bc160 100644 --- a/lib/symbols/flutter_symbols.dart +++ b/lib/symbols/flutter_symbols.dart @@ -84,11 +84,11 @@ ArgParser buildParserSymbols() { ) ..addOption( 'app-id', - help: 'Raygun\'s application ID', + help: 'Raygun application ID', ) ..addOption( 'token', - help: 'Raygun\'s access token', + help: 'Raygun access token', ) ..addOption( 'path',