diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a2c091803..bbb9ad79ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - [#2539: Remove dependency on `sync-request`](https://github.com/alphagov/govuk-prototype-kit/pull/2539) - [#2540: Remove dependency on `require-dir`](https://github.com/alphagov/govuk-prototype-kit/pull/2540) - [#2543: Update `qs`, `ws`, `body-parser`, `express`, `engine.io`, `engine.io-client` and `socket.io-adapter`](https://github.com/alphagov/govuk-prototype-kit/pull/2543) +- [#2545: Remove usage data tracking](https://github.com/alphagov/govuk-prototype-kit/pull/2545) ## 13.20.1 diff --git a/__tests__/spec/sanity-checks.js b/__tests__/spec/sanity-checks.js index b51b1d31e6..7729238fb2 100644 --- a/__tests__/spec/sanity-checks.js +++ b/__tests__/spec/sanity-checks.js @@ -25,7 +25,7 @@ const createKitTimeout = parseInt(process.env.CREATE_KIT_TIMEOUT || '90000', 10) */ describe('The Prototype Kit', () => { beforeAll(async () => { - await mkPrototype(tmpDir, { allowTracking: false, overwrite: true }) + await mkPrototype(tmpDir, { overwrite: true }) const { setPackagesCache } = require('../../lib/plugins/packages') setPackagesCache([{ diff --git a/__tests__/utils/index.js b/__tests__/utils/index.js index 6b6bc931b1..536e6bf2ec 100644 --- a/__tests__/utils/index.js +++ b/__tests__/utils/index.js @@ -42,9 +42,6 @@ function mkdtempSync () { * @param {Object} [options] * @param {string} [options.kitPath] - Path to the kit to use when creating prototype, if not provided uses mkReleaseArchive * @param {boolean} [options.overwrite] - Allow existing prototype to be overwritten (optional) - * @param {boolean} [options.allowTracking] - If undefined no usage-data-config.json is created (optional), - * if true a usage-data-config.json is created allowing tracking, - * if false a usage-data-config.json is crated disallowing tracking * @param {boolean} [options.npmInstallLinks] - Set value for npm config install-links (optional) * @param {string} [options.commandLineParameters] - Command line parameters (optional) * @returns {Promise} @@ -52,7 +49,6 @@ function mkdtempSync () { async function mkPrototype (prototypePath, { kitPath, overwrite = false, - allowTracking = undefined, npmInstallLinks = undefined, commandLineParameters = '' } = {}) { // TODO: Use kitPath if provided @@ -85,10 +81,6 @@ async function mkPrototype (prototypePath, { { cwd: repoDir, env: execEnv, stdio: 'inherit' } ) - if (allowTracking !== undefined) { - await fse.writeJson(path.join(prototypePath, 'usage-data-config.json'), { collectUsageData: !!allowTracking }) - } - process.stderr.write(`Kit creation took [${Math.round((Date.now() - startTime) / 100) / 10}] seconds\n`) } catch (error) { console.error(error.message) diff --git a/bin/cli b/bin/cli index eac1e773b9..2d82bcfe95 100755 --- a/bin/cli +++ b/bin/cli @@ -77,7 +77,6 @@ node_modules/ .tmp/ .env migrate.log -usage-data-config.json # General ignores .DS_Store diff --git a/cypress/scripts/run-starter-prototype.js b/cypress/scripts/run-starter-prototype.js index 79667f1b40..b96845d0e3 100644 --- a/cypress/scripts/run-starter-prototype.js +++ b/cypress/scripts/run-starter-prototype.js @@ -10,7 +10,7 @@ const defaultKitPath = path.join(os.tmpdir(), 'cypress', 'test-prototype') const testDir = path.resolve(process.env.KIT_TEST_DIR || defaultKitPath) ;(async () => { - await mkPrototype(testDir, { overwrite: true, allowTracking: false, npmInstallLinks: true }) + await mkPrototype(testDir, { overwrite: true, npmInstallLinks: true }) const fooLocation = path.join(__dirname, '..', 'fixtures', 'plugins', 'plugin-foo') const barLocation = path.join(__dirname, '..', 'fixtures', 'plugins', 'plugin-bar') diff --git a/lib/dev-server.js b/lib/dev-server.js index a5ae807afa..d967e9305b 100644 --- a/lib/dev-server.js +++ b/lib/dev-server.js @@ -16,7 +16,6 @@ const { setNodemonInstance } = require('./build') const plugins = require('./plugins/plugins') -const { collectDataUsage } = require('./usage-data') const utils = require('./utils') const { packageDir, @@ -32,7 +31,6 @@ const { logPerformanceSummaryOnce, startPerformanceTimer, endPerformanceTimer } // Build watch and serve async function runDevServer () { - await collectDataUsage() let startupError try { diff --git a/lib/usage-data.js b/lib/usage-data.js deleted file mode 100644 index 536a5874f0..0000000000 --- a/lib/usage-data.js +++ /dev/null @@ -1,120 +0,0 @@ -// core dependencies -const fs = require('fs') -const os = require('os') -const path = require('path') -const { randomUUID } = require('node:crypto') - -// npm dependencies -const { default: confirm } = require('@inquirer/confirm') -const universalAnalytics = require('universal-analytics') -const ansiColors = require('ansi-colors') - -// local dependencies -const packageJson = require('../package.json') -const { projectDir } = require('./utils/paths') - -const usageDataFilePath = path.join(projectDir, 'usage-data-config.json') -const headline = ansiColors.underline('Help us improve the GOV.UK Prototype Kit') - -const USAGE_DATA_PROMPT = ` -${headline} - -With your permission, the kit can send useful anonymous usage data -for analysis to help the team improve the service. Read more here: -https://prototype-kit.service.gov.uk/docs/usage-data - -Do you give permission for the kit to send anonymous usage data? -`.trim() - -function getUsageDataConfig () { - // Try to read config file to see if usage data is opted in - let usageDataConfig = {} - try { - usageDataConfig = require(usageDataFilePath) - } catch (e) { - // do nothing - we will make a config - } - return usageDataConfig -} - -function setUsageDataConfig (usageDataConfig) { - const usageDataConfigJSON = JSON.stringify(usageDataConfig, null, ' ') - try { - fs.writeFileSync(usageDataFilePath, usageDataConfigJSON) - return true - } catch (error) { - console.error(error) - } - return false -} - -// Ask for permission to track data -// returns a Promise with the user's answer -async function askForUsageDataPermission () { - if (process.stdout.isTTY) { - return confirm({ - message: USAGE_DATA_PROMPT, - default: false - }) - } - - return false -} - -function startTracking (usageDataConfig) { - // Get client ID for tracking - // https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#cid - - if (usageDataConfig.clientId === undefined) { - usageDataConfig.clientId = randomUUID() - module.exports.setUsageDataConfig(usageDataConfig) - } - - // Track kit start event, with kit version, operating system and Node.js version - const trackingId = 'UA-26179049-21' - const trackingUser = universalAnalytics(trackingId, usageDataConfig.clientId) - - const kitVersion = packageJson.version - const operatingSystem = os.platform() + ' ' + os.release() - const nodeVersion = process.versions.node - - // Anonymise the IP - trackingUser.set('anonymizeIp', 1) - - // Set custom dimensions - trackingUser.set('cd1', operatingSystem) - trackingUser.set('cd2', kitVersion) - trackingUser.set('cd3', nodeVersion) - - // Trigger start event - trackingUser.event('State', 'Start').send() -} - -async function collectDataUsage () { - // Get usageDataConfig from file, if exists - const usageDataConfig = module.exports.getUsageDataConfig() - - if (usageDataConfig.collectUsageData === undefined) { - // No recorded answer, so ask for permission - const permissionGranted = await module.exports.askForUsageDataPermission() - usageDataConfig.collectUsageData = permissionGranted - module.exports.setUsageDataConfig(usageDataConfig) - - if (permissionGranted) { - module.exports.startTracking(usageDataConfig) - } - } else { - if (usageDataConfig.collectUsageData === true) { - // Opted in - module.exports.startTracking(usageDataConfig) - } - } -} - -module.exports = { - getUsageDataConfig, - setUsageDataConfig, - askForUsageDataPermission, - startTracking, - collectDataUsage -} diff --git a/lib/usage-data.test.js b/lib/usage-data.test.js deleted file mode 100644 index 65cd7efd5f..0000000000 --- a/lib/usage-data.test.js +++ /dev/null @@ -1,77 +0,0 @@ -const { askForUsageDataPermission } = require('./usage-data') - -jest.mock('@inquirer/confirm') - -const { default: confirm } = require('@inquirer/confirm') - -describe('askForUsageDataPermission', () => { - afterEach(() => { - jest.resetAllMocks() - }) - - describe('when in an interactive shell', () => { - // We can't use jest.replaceProperty here because in a non-TTY environment - // the isTTY property doesn't exist on process.stdout, and Jest will only - // replace existing properties. - let originalTTY - - beforeAll(() => { - originalTTY = process.stdout.isTTY - process.stdout.isTTY = true - }) - - afterAll(() => { - process.stdout.isTTY = originalTTY - }) - - it('asks the user for confirmation', () => { - askForUsageDataPermission() - - expect(confirm).toHaveBeenCalledTimes(1) - expect(confirm).toHaveBeenCalledWith({ - default: false, - message: expect.stringContaining( - 'Do you give permission for the kit to send anonymous usage data?' - ) - }) - }) - - it('returns true if the user accepts tracking', async () => { - confirm.mockResolvedValue(true) - - expect(await askForUsageDataPermission()).toBe(true) - }) - - it('returns false if the user does not accept tracking', async () => { - confirm.mockResolvedValue(false) - - expect(await askForUsageDataPermission()).toBe(false) - }) - }) - - describe('when in a non-interactive shell', () => { - // We can't use jest.replaceProperty here because in a non-TTY environment - // the isTTY property doesn't exist on process.stdout, and Jest will only - // replace existing properties. - let originalTTY - - beforeAll(() => { - originalTTY = process.stdout.isTTY - process.stdout.isTTY = undefined - }) - - afterAll(() => { - process.stdout.isTTY = originalTTY - }) - - it('does not ask the user for confirmation', () => { - askForUsageDataPermission() - - expect(confirm).not.toHaveBeenCalled() - }) - - it('returns false', async () => { - expect(await askForUsageDataPermission()).toBe(false) - }) - }) -}) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 05cb3c27e1..8d9297b6c2 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -29,8 +29,7 @@ "portscanner": "^2.2.0", "sass": "^1.89.2", "semver": "^7.7.2", - "tar-stream": "^3.1.7", - "universal-analytics": "^0.5.3" + "tar-stream": "^3.1.7" }, "bin": { "govuk-prototype-kit": "bin/cli" @@ -11632,40 +11631,6 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, - "node_modules/universal-analytics": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.5.3.tgz", - "integrity": "sha512-HXSMyIcf2XTvwZ6ZZQLfxfViRm/yTGoRgDeTbojtq6rezeyKB0sTBcKH2fhddnteAHRcHiKgr/ACpbgjGOC6RQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.1", - "uuid": "^8.0.0" - }, - "engines": { - "node": ">=12.18.2" - } - }, - "node_modules/universal-analytics/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/universal-analytics/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -11754,6 +11719,7 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "dev": true, "license": "MIT", "bin": { "uuid": "dist/bin/uuid" @@ -20417,30 +20383,6 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" }, - "universal-analytics": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.5.3.tgz", - "integrity": "sha512-HXSMyIcf2XTvwZ6ZZQLfxfViRm/yTGoRgDeTbojtq6rezeyKB0sTBcKH2fhddnteAHRcHiKgr/ACpbgjGOC6RQ==", - "requires": { - "debug": "^4.3.1", - "uuid": "^8.0.0" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -20494,7 +20436,8 @@ "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true }, "v8-to-istanbul": { "version": "9.1.3", diff --git a/package.json b/package.json index 2ac0f9971f..14d140a1ac 100644 --- a/package.json +++ b/package.json @@ -82,8 +82,7 @@ "portscanner": "^2.2.0", "sass": "^1.89.2", "semver": "^7.7.2", - "tar-stream": "^3.1.7", - "universal-analytics": "^0.5.3" + "tar-stream": "^3.1.7" }, "devDependencies": { "cheerio": "^1.0.0-rc.12", diff --git a/scripts/create-prototype-and-run.js b/scripts/create-prototype-and-run.js index 4bf46c1b76..59615f08e6 100644 --- a/scripts/create-prototype-and-run.js +++ b/scripts/create-prototype-and-run.js @@ -22,7 +22,7 @@ console.log(`running prototype using command "${command}"`) console.log() // noinspection JSVoidFunctionReturnValueUsed -mkPrototype(testDir, { overwrite: true, allowTracking: false }) +mkPrototype(testDir, { overwrite: true }) .then(() => { console.log() return exec(command, { cwd: testDir, env: { ...process.env, env: 'test' }, stdio: 'inherit' }) diff --git a/scripts/performance-test-prepare b/scripts/performance-test-prepare index fa2d6f5689..1c01603582 100755 --- a/scripts/performance-test-prepare +++ b/scripts/performance-test-prepare @@ -18,7 +18,6 @@ cd tmp/performance-testing rm -Rf govuk-prototype-kit-performance-kit git clone git@github.com:alphagov/govuk-prototype-kit-performance-kit.git cd govuk-prototype-kit-performance-kit -echo '{"collectUsageData": false}' > usage-data-config.json npm install $dependency --save-exact npm install npm run dev diff --git a/scripts/refresh-companion-kit b/scripts/refresh-companion-kit index 21ce259f46..11b9e6063f 100755 --- a/scripts/refresh-companion-kit +++ b/scripts/refresh-companion-kit @@ -1,4 +1,3 @@ #!/bin/sh mkdir -p ~/projects/prototype-kits/ && rm -Rf ~/projects/prototype-kits/companion-prototype-kit && ./bin/cli create --version=local-symlink ~/projects/prototype-kits/companion-prototype-kit --use-njk-extensions -echo '{"collectUsageData": false}' > ~/projects/prototype-kits/companion-prototype-kit/usage-data-config.json