-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor telemetry-consent file and add tests #5557
Merged
ChristopherDedominici
merged 1 commit into
v-next
from
features/improve-telemetry-consent-functions
Jul 29, 2024
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 0 additions & 78 deletions
78
v-next/hardhat/src/internal/cli/telemetry/telemetry-consent.ts
This file was deleted.
Oops, something went wrong.
117 changes: 117 additions & 0 deletions
117
v-next/hardhat/src/internal/cli/telemetry/telemetry-permissions.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import path from "node:path"; | ||
|
||
import { getConfigDir } from "@ignored/hardhat-vnext-core/global-dir"; | ||
import { isCi } from "@ignored/hardhat-vnext-utils/ci"; | ||
import { | ||
exists, | ||
readJsonFile, | ||
writeJsonFile, | ||
} from "@ignored/hardhat-vnext-utils/fs"; | ||
|
||
import { confirmationPromptWithTimeout } from "../prompt/prompt.js"; | ||
|
||
interface TelemetryConsent { | ||
consent: boolean; | ||
} | ||
|
||
/** | ||
* Ensure that the user's telemetry consent is set. If the consent is already provided, returns the answer. | ||
* If not, prompts the user to provide it. | ||
* Consent is only asked in interactive environments. | ||
* | ||
* @returns True if the user consents to telemetry and if current environment supports telemetry, false otherwise. | ||
*/ | ||
export async function ensureTelemetryConsent(): Promise<boolean> { | ||
if (!isTelemetryAllowedInEnvironment()) { | ||
return false; | ||
} | ||
|
||
const consent = await getTelemetryConsent(); | ||
if (consent !== undefined) { | ||
return consent; | ||
} | ||
|
||
// Telemetry consent not provided yet, ask for it | ||
return requestTelemetryConsent(); | ||
} | ||
|
||
/** | ||
* Checks whether telemetry is supported in the current environment and whether the user has provided consent. | ||
* | ||
* @returns True if the user consents to telemetry and if current environment supports telemetry, false otherwise. | ||
*/ | ||
export async function isTelemetryAllowed(): Promise<boolean> { | ||
if (!isTelemetryAllowedInEnvironment()) { | ||
return false; | ||
} | ||
|
||
const consent = await getTelemetryConsent(); | ||
return consent !== undefined ? consent : false; | ||
} | ||
|
||
/** | ||
* Retrieves the user's telemetry consent status from the consent file. | ||
* | ||
* @returns True if the user consents to telemetry, false if they do not consent, | ||
* and undefined if no consent has been provided. | ||
*/ | ||
export async function getTelemetryConsent(): Promise<boolean | undefined> { | ||
const telemetryConsentFilePath = await getTelemetryConsentFilePath(); | ||
|
||
if (await exists(telemetryConsentFilePath)) { | ||
// Telemetry consent was already provided, hence return the answer | ||
return (await readJsonFile<TelemetryConsent>(telemetryConsentFilePath)) | ||
.consent; | ||
} | ||
|
||
return undefined; | ||
} | ||
|
||
/** | ||
* Determines if telemetry is allowed in the current environment. | ||
* This function checks various environmental factors to decide if telemetry data can be collected. | ||
* It verifies that the environment is not a continuous integration (CI) environment, that the terminal is interactive, | ||
* and that telemetry has not been explicitly disabled through an environment variable. | ||
* | ||
* @returns True if telemetry is allowed in the environment, false otherwise. | ||
*/ | ||
export function isTelemetryAllowedInEnvironment(): boolean { | ||
return ( | ||
(!isCi() && | ||
process.stdout.isTTY === true && | ||
process.env.HARDHAT_DISABLE_TELEMETRY_PROMPT !== "true") || | ||
process.env.HARDHAT_ENABLE_TELEMETRY_IN_TEST === "true" // Used in tests to force telemetry execution | ||
); | ||
} | ||
|
||
async function getTelemetryConsentFilePath() { | ||
const configDir = await getConfigDir(); | ||
return path.join(configDir, "telemetry-consent.json"); | ||
} | ||
|
||
async function requestTelemetryConsent(): Promise<boolean> { | ||
const consent = await confirmTelemetryConsent(); | ||
|
||
if (consent === undefined) { | ||
return false; | ||
} | ||
|
||
// Store user's consent choice | ||
await writeJsonFile(await getTelemetryConsentFilePath(), { consent }); | ||
|
||
// TODO: this will be enabled in a following PR as soon as the function to send telemetry is implemented | ||
// const subprocessFilePath = path.join( | ||
// path.dirname(fileURLToPath(import.meta.url)), | ||
// "report-telemetry-consent.js", | ||
// ); | ||
// await spawnDetachedSubProcess(subprocessFilePath, [consent ? "yes" : "no"]); | ||
|
||
return consent; | ||
} | ||
|
||
async function confirmTelemetryConsent(): Promise<boolean | undefined> { | ||
return confirmationPromptWithTimeout( | ||
"telemetryConsent", | ||
"Help us improve Hardhat with anonymous crash reports & basic usage data?", | ||
); | ||
} |
78 changes: 78 additions & 0 deletions
78
v-next/hardhat/test/internal/cli/telemetry/telemetry-permissions.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import assert from "node:assert/strict"; | ||
import path from "node:path"; | ||
import { afterEach, beforeEach, describe, it } from "node:test"; | ||
|
||
import { getConfigDir } from "@ignored/hardhat-vnext-core/global-dir"; | ||
import { remove, writeJsonFile } from "@ignored/hardhat-vnext-utils/fs"; | ||
|
||
import { | ||
getTelemetryConsent, | ||
isTelemetryAllowed, | ||
} from "../../../../src/internal/cli/telemetry/telemetry-permissions.js"; | ||
|
||
async function setTelemetryConsentFile(consent: boolean) { | ||
const configDir = await getConfigDir(); | ||
const filePath = path.join(configDir, "telemetry-consent.json"); | ||
await writeJsonFile(filePath, { consent }); | ||
} | ||
|
||
async function deleteTelemetryConsentFile() { | ||
const configDir = await getConfigDir(); | ||
const filePath = path.join(configDir, "telemetry-consent.json"); | ||
await remove(filePath); | ||
} | ||
|
||
describe("telemetry-permissions", () => { | ||
beforeEach(async () => { | ||
delete process.env.HARDHAT_ENABLE_TELEMETRY_IN_TEST; | ||
|
||
await deleteTelemetryConsentFile(); | ||
}); | ||
|
||
afterEach(async () => { | ||
delete process.env.HARDHAT_ENABLE_TELEMETRY_IN_TEST; | ||
|
||
await deleteTelemetryConsentFile(); | ||
}); | ||
|
||
describe("isTelemetryAllowed", () => { | ||
it("should return false because not an interactive environment", async () => { | ||
await setTelemetryConsentFile(true); // Needed to be sure that the file is not read and the process exits before | ||
|
||
const res = await isTelemetryAllowed(); | ||
assert.equal(res, false); | ||
}); | ||
|
||
it("should return false because the user did not give telemetry consent", async () => { | ||
process.env.HARDHAT_ENABLE_TELEMETRY_IN_TEST = "true"; | ||
await setTelemetryConsentFile(false); | ||
|
||
const res = await isTelemetryAllowed(); | ||
assert.equal(res, false); | ||
}); | ||
|
||
it("should return false because the telemetry consent is not set", async () => { | ||
process.env.HARDHAT_ENABLE_TELEMETRY_IN_TEST = "true"; | ||
|
||
const res = await isTelemetryAllowed(); | ||
assert.equal(res, false); | ||
}); | ||
|
||
it("should return true because the user gave telemetry consent", async () => { | ||
process.env.HARDHAT_ENABLE_TELEMETRY_IN_TEST = "true"; | ||
await setTelemetryConsentFile(true); | ||
|
||
const res = await isTelemetryAllowed(); | ||
assert.equal(res, true); | ||
}); | ||
}); | ||
|
||
it("should return undefined because the telemetry consent is not set", async () => { | ||
// All other possible results are tested in the previous tests, they are included in the the function 'isTelemetryAllowed' | ||
process.env.HARDHAT_ENABLE_TELEMETRY_IN_TEST = "true"; | ||
|
||
const res = await getTelemetryConsent(); | ||
|
||
assert.equal(res, undefined); | ||
}); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the new logic, the telemetry consent will be read directly when needed.
This function purpose is to ensure that telemetry-consent is registered, so no value needs to be returned