diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f36f81d198..4a414dd40b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -252,8 +252,8 @@ deploy-staging: - export BUILD_MODE=canary - yarn - yarn build:bundle - - ./scripts/deploy.sh staging staging - - node ./scripts/upload-source-maps.js datad0g.com,datadoghq.com staging + - node ./scripts/deploy.js staging staging + - node ./scripts/upload-source-maps.js staging deploy-prod-canary: stage: deploy:canary @@ -264,8 +264,8 @@ deploy-prod-canary: - export BUILD_MODE=canary - yarn - yarn build:bundle - - ./scripts/deploy.sh prod canary - - node ./scripts/upload-source-maps.js datadoghq.com canary + - node ./scripts/deploy.js prod canary + - node ./scripts/upload-source-maps.js canary deploy-prod-stable: stage: deploy @@ -279,8 +279,8 @@ deploy-prod-stable: - VERSION=$(node -p -e "require('./lerna.json').version") - yarn - yarn build:bundle - - ./scripts/deploy.sh prod v${VERSION%%.*} - - node ./scripts/upload-source-maps.js datadoghq.com,datadoghq.eu,us3.datadoghq.com,us5.datadoghq.com,ap1.datadoghq.com v${VERSION%%.*} + - node ./scripts/deploy.js prod v${VERSION%%.*} + - node ./scripts/upload-source-maps.js v${VERSION%%.*} publish-npm: stage: deploy diff --git a/scripts/deploy.js b/scripts/deploy.js new file mode 100644 index 0000000000..1c76879ab2 --- /dev/null +++ b/scripts/deploy.js @@ -0,0 +1,75 @@ +'use strict' + +const { printLog, command, runMain } = require('./utils') + +const ONE_MINUTE_IN_SECOND = 60 +const ONE_HOUR_IN_SECOND = 60 * ONE_MINUTE_IN_SECOND +const AWS_CONFIG = { + prod: { + accountId: 464622532012, + bucketName: 'browser-agent-artifacts-prod', + distributionId: 'EGB08BYCT1DD9', + }, + staging: { + accountId: 727006795293, + bucketName: 'browser-agent-artifacts-staging', + distributionId: 'E2FP11ZSCFD3EU', + }, +} +/** + * Deploy SDK files to CDN + * Usage: + * node deploy.js staging|prod staging|canary|vXXX + */ +const env = process.argv[2] +const version = process.argv[3] + +const bundles = { + 'packages/rum/bundle/datadog-rum.js': `datadog-rum-${version}.js`, + 'packages/rum-slim/bundle/datadog-rum-slim.js': `datadog-rum-slim-${version}.js`, + 'packages/logs/bundle/datadog-logs.js': `datadog-logs-${version}.js`, +} + +runMain(() => { + uploadToS3(AWS_CONFIG[env]) + invalidateCloudfront(AWS_CONFIG[env]) +}) + +function uploadToS3(awsConfig) { + const accessToS3 = generateEnvironmentForRole(awsConfig.accountId, 'build-stable-browser-agent-artifacts-s3-write') + const browserCache = + version === 'staging' || version === 'canary' ? 15 * ONE_MINUTE_IN_SECOND : 4 * ONE_HOUR_IN_SECOND + const cacheControl = `max-age=${browserCache}, s-maxage=60` + + for (const [filePath, bundleName] of Object.entries(bundles)) { + printLog(`Upload ${filePath} to s3://${awsConfig.bucketName}/${bundleName}`) + command` + aws s3 cp --cache-control ${cacheControl} ${filePath} s3://${awsConfig.bucketName}/${bundleName}` + .withEnvironment(accessToS3) + .run() + } +} + +function invalidateCloudfront(awsConfig) { + const accessToCloudfront = generateEnvironmentForRole(awsConfig.accountId, 'build-stable-cloudfront-invalidation') + const pathsToInvalidate = Object.values(bundles).map((path) => `/${path}`) + + printLog(`Trigger invalidation on ${awsConfig.distributionId} for: ${pathsToInvalidate.join(', ')}`) + command` + aws cloudfront create-invalidation --distribution-id ${awsConfig.distributionId} --paths ${pathsToInvalidate}` + .withEnvironment(accessToCloudfront) + .run() +} + +function generateEnvironmentForRole(awsAccountId, roleName) { + const rawCredentials = command` + aws sts assume-role + --role-arn arn:aws:iam::${awsAccountId}:role/${roleName} + --role-session-name AWSCLI-Session`.run() + const credentials = JSON.parse(rawCredentials)['Credentials'] + return { + AWS_ACCESS_KEY_ID: credentials['AccessKeyId'], + AWS_SECRET_ACCESS_KEY: credentials['SecretAccessKey'], + AWS_SESSION_TOKEN: credentials['SessionToken'], + } +} diff --git a/scripts/deploy.sh b/scripts/deploy.sh deleted file mode 100755 index 8ba5726969..0000000000 --- a/scripts/deploy.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -env=$1 -suffix=$2 - -USAGE="Usage: ./deploy.sh staging|prod staging|canary|vXXX" - -case "${env}" in -"prod") - AWS_ACCOUNT_ID=464622532012 - BUCKET_NAME="browser-agent-artifacts-prod" - DISTRIBUTION_ID="EGB08BYCT1DD9" - ;; -"staging") - AWS_ACCOUNT_ID=727006795293 - BUCKET_NAME="browser-agent-artifacts-staging" - DISTRIBUTION_ID="E2FP11ZSCFD3EU" - ;; -* ) - echo $USAGE - exit 1 - ;; -esac - -case "${suffix}" in -v[0-9]*) - CACHE_CONTROL='max-age=14400, s-maxage=60' - ;; -"canary" | "staging") - CACHE_CONTROL='max-age=900, s-maxage=60' - ;; -* ) - echo $USAGE - exit 1 - ;; -esac - -declare -A BUNDLES=( - ["datadog-rum-${suffix}.js"]="packages/rum/bundle/datadog-rum.js" - ["datadog-rum-slim-${suffix}.js"]="packages/rum-slim/bundle/datadog-rum-slim.js" - ["datadog-logs-${suffix}.js"]="packages/logs/bundle/datadog-logs.js" -) - -main() { - in-isolation upload-to-s3 - in-isolation invalidate-cloudfront -} - -upload-to-s3() { - assume-role "build-stable-browser-agent-artifacts-s3-write" - for bundle_name in "${!BUNDLES[@]}"; do - local file_path=${BUNDLES[$bundle_name]} - echo "Upload ${bundle_name}" - aws s3 cp --cache-control "$CACHE_CONTROL" "$file_path" s3://${BUCKET_NAME}/${bundle_name}; - done -} - -invalidate-cloudfront() { - assume-role "build-stable-cloudfront-invalidation" - echo "Creating invalidation" - local -a paths_to_invalidate - for bundle_name in "${!BUNDLES[@]}"; do - paths_to_invalidate+=("/$bundle_name") - done - aws cloudfront create-invalidation --distribution-id ${DISTRIBUTION_ID} --paths "${paths_to_invalidate[@]}" -} - -in-isolation() { - function=$1 - # subshell to assume-role only for the function - ( - ${function} - ) -} - -assume-role() { - set +x - role_name=$1 - temp_credentials=$(aws sts assume-role --role-arn "arn:aws:iam::${AWS_ACCOUNT_ID}:role/${role_name}" --role-session-name AWSCLI-Session) - export AWS_ACCESS_KEY_ID=$(echo "$temp_credentials" | jq -r .Credentials.AccessKeyId) - export AWS_SECRET_ACCESS_KEY=$(echo "$temp_credentials" | jq -r .Credentials.SecretAccessKey) - export AWS_SESSION_TOKEN=$(echo "$temp_credentials" | jq -r .Credentials.SessionToken) - set -x -} - -main diff --git a/scripts/upload-source-maps.js b/scripts/upload-source-maps.js index b006dde650..bf67a65d75 100644 --- a/scripts/upload-source-maps.js +++ b/scripts/upload-source-maps.js @@ -6,25 +6,43 @@ const { SDK_VERSION } = require('./build-env') /** * Upload source maps to datadog * Usage: - * BUILD_MODE=canary|release node upload-source-maps.js site1,site2,... staging|canary|vXXX + * BUILD_MODE=canary|release node upload-source-maps.js staging|canary|vXXX */ -const sites = process.argv[2].split(',') -const suffix = process.argv[3] +const version = process.argv[2] const packages = [ { name: 'logs', service: 'browser-logs-sdk' }, { name: 'rum', service: 'browser-rum-sdk' }, { name: 'rum-slim', service: 'browser-rum-sdk' }, ] +const sitesByVersion = { + staging: ['datad0g.com', 'datadoghq.com'], + canary: ['datadoghq.com'], + v4: ['datadoghq.com', 'datadoghq.eu', 'us3.datadoghq.com', 'us5.datadoghq.com', 'ap1.datadoghq.com'], +} + +runMain(() => { + for (const { name, service } of packages) { + const bundleFolder = `packages/${name}/bundle` + renameFilesWithVersionSuffix(bundleFolder, name) + for (const site of sitesByVersion[version]) { + const normalizedSite = site.replaceAll('.', '-') + const apiKey = getSecretKey(`ci.browser-sdk.source-maps.${normalizedSite}.ci_api_key`) + + uploadSourceMaps(site, apiKey, name, service, bundleFolder) + } + } + printLog('Source maps upload done.') +}) -function renameFiles(bundleFolder, packageName) { +function renameFilesWithVersionSuffix(bundleFolder, packageName) { // The datadog-ci CLI is taking a directory as an argument. It will scan every source map files in // it and upload those along with the minified bundle. The file names must match the one from the // CDN, thus we need to rename the bundles with the right suffix. for (const ext of ['js', 'js.map']) { - const filePath = `${bundleFolder}/datadog-${packageName}.${ext}` - const suffixedFilePath = `${bundleFolder}/datadog-${packageName}-${suffix}.${ext}` - command`mv ${filePath} ${suffixedFilePath}`.run() + const sourceFilePath = `${bundleFolder}/datadog-${packageName}.${ext}` + const targetFilePath = `${bundleFolder}/datadog-${packageName}-${version}.${ext}` + command`mv ${sourceFilePath} ${targetFilePath}`.run() } } @@ -45,17 +63,3 @@ function uploadSourceMaps(site, apiKey, packageName, service, bundleFolder) { }) .run() } - -runMain(() => { - for (const { name, service } of packages) { - const bundleFolder = `packages/${name}/bundle` - renameFiles(bundleFolder, name) - for (const site of sites) { - const normalizedSite = site.replaceAll('.', '-') - const apiKey = getSecretKey(`ci.browser-sdk.source-maps.${normalizedSite}.ci_api_key`) - - uploadSourceMaps(site, apiKey, name, service, bundleFolder) - } - } - printLog('Source maps upload done.') -}) diff --git a/scripts/utils.js b/scripts/utils.js index 9c5ec80836..1d01509950 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -164,6 +164,11 @@ function command(...templateArguments) { * * parseCommandTemplateArguments`foo ${'bar baz'}` == ['foo', 'bar baz'] * + * To pass template variables as different command arguments use an array as template argument: + * + * parseCommandTemplateArguments`foo ${['bar', 'baz']}` == ['foo', 'bar', 'baz'] + * + * * const commitMessage = 'my commit message' * parseCommandTemplateArguments`git commit -c ${commitMessage}` == ['git', 'commit', '-c', 'my commit message'] * @@ -175,8 +180,13 @@ function parseCommandTemplateArguments(templateStrings, ...templateVariables) { if (i > 0) { // Interleave variables with template strings if (!parsedArguments.length || templateStrings[i - 1].match(/\s$/)) { - // If the latest string ends with a space, consider the variable as a separate argument - parsedArguments.push(templateVariables[i - 1]) + // If the latest string ends with a space, consider the variable as separate argument(s) + const variable = templateVariables[i - 1] + if (Array.isArray(variable)) { + parsedArguments.push(...variable) + } else { + parsedArguments.push(variable) + } } else { // Else, append the variable to the latest argument parsedArguments[parsedArguments.length - 1] += templateVariables[i - 1]