Deploy your source maps to Rollbar with Ansible
Posted on 2018-07-16 in Trucs et astuces
I recently had to integrate Rollbar (a service to track errors) with a JS application. One nice thing about Rollbar is that if you provide it with source maps, it will point to you where the error is in the original unminified code.
Since the application is deployed with Ansible, I wanted to upload the source maps with Ansible. There is a built in module to register deploys with Ansible: rollbar_deployment but that only solves half the problem since it only registers deployments. Example of usage:
- name: Register deploy rollbar_deployment: token: "{{ lookup('env','ROLLBAR_API_KEY') }}" environment: "{{ env }}" revision: "{{ version }}"
with:
- {{ env }} the environment you are deploying to.
- {{ version }} the revision you want to register.
- You also need the ROLLBAR_API_KEY to be set and to contain your Rollbar server API key so you can authenticate to Rollbar.
Since I didn't found a module already done, I decide to build my own.
Below is the code of the Ansible module. Place it under library/rollbar_source_maps.py in your Ansible project. Note: it only works with Python 3+ and requires the requests library.
You will then be able to use it with:
- hosts: fronts tasks: - name: Upload source maps to Rollbar connection: local rollbar_source_maps: src: "{{ src }}/dist/scripts" base_url: "{{ base_url }}" version: "{{ version }}" token: "{{ lookup('env','ROLLBAR_API_KEY') }}"
Where:
- {{ src }} points to the root of your JS project. The src key in the YAML must point to the folder that contains your source maps. Feel free to adapt to your path.
- {{ base_url }} points to the base URL of your scripts. So, if they are all located under http://aot.testing/dist/scripts/ that what you must use here.
- {{ version }} is the current version of the code you are deploying so Rollbar can track your deploys and help you find the one that caused a problem.
- You also need the ROLLBAR_API_KEY to be set and to contain your Rollbar server API key so you can authenticate to Rollbar.
#!/usr/bin/python3 # Copyright 2018, Julien Enselme, <jujens@jujens.eu> # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ANSIBLE_METADATA = { 'metadata_version': '1.0', 'status': ['preview'], 'supported_by': 'community', } DOCUMENTATION = ''' --- module: rollbar_source_maps version_added: 1.0 author: "Julien Enselme (@Jenselme)" short_description: Upload source maps to Rollbar description: - Upload source maps from a folder to Rollbar. We associate scripts and source maps thanks to the base URL and the name of the files. A source map must have the same name as the files and ends with .map (see https://docs.rollbar.com/docs/source-maps) options: token: description: - Your project access token. required: true src: description: - The directory in which to look for source maps. required: true base_url: description: - The base URL of your site so Rollbar can have the mapping of a file on the site to a file on the local system. required: true version: description: - The version of the source maps. required: true url: description: - Optional URL to submit the notification to. required: false default: 'https://api.rollbar.com/api/1/sourcemap' validate_certs: description: - If C(no), SSL certificates for the target url will not be validated. This should only be used on personally controlled sites using self-signed certificates. required: false default: 'yes' choices: ['yes', 'no'] ''' EXAMPLES = ''' - name: Upload source maps to Rollbar rollbar_source_maps: src: dist base_url: http://www.example.com/scripts/ version: v1.0.0 token: AAAA ''' import os import traceback from pathlib import Path from urllib.parse import urljoin import requests from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.six.moves.urllib.parse import urlencode from ansible.module_utils._text import to_native from ansible.module_utils.urls import fetch_url def main(): module = AnsibleModule( argument_spec=dict( token=dict(required=True), src=dict(required=True), base_url=dict(required=True), version=dict(required=True), url=dict( required=False, default='https://api.rollbar.com/api/1/sourcemap' ), validate_certs=dict(default='yes', type='bool'), ), supports_check_mode=True, ) if module.check_mode: module.exit_json(changed=True) params = dict( access_token=module.params['token'], ) base_url = module.params.get('base_url') version = module.params.get('version') url = module.params.get('url') src = module.params.get('src') for file in Path(src).glob('*.js.map'): with file.open('rb') as source_map: source_map_content = source_map.read() minified_url = urljoin(base_url, file.name).replace('.map', '') try: resp = requests.post(url, files={ 'source_map': source_map_content, }, data={ 'access_token': module.params.get('token'), 'minified_url': minified_url, 'version': version, }) except Exception as e: module.fail_json(msg='Unable to upload source maps to Rollbar: %s' % to_native(e), exception=traceback.format_exc()) else: if resp.status_code != 200: module.fail_json(msg='HTTP result code: %d connecting to %s with error %s' % (resp.status_code, url, resp.text)) module.exit_json(changed=True) if __name__ == '__main__': main()