Support user-defined diagnostics attribution (#635)

* Switch to flakehub

* Diagnostics: support user-defined attribution.

Allows a user to specify an additional value to associate their diagnostics with that value. nix-installer doesn't generate or store these values, and most users have no need for it.

* Respond to feedback
This commit is contained in:
Graham Christensen 2023-09-20 14:23:23 -04:00 committed by GitHub
parent 1c103edb90
commit 60e5fff623
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 70 additions and 30 deletions

View file

@ -465,6 +465,7 @@ Here is a table of the [diagnostic data we collect][diagnosticdata]:
| `is_ci` | Whether the installer is being used in CI (e.g. GitHub Actions). | | `is_ci` | Whether the installer is being used in CI (e.g. GitHub Actions). |
| `action` | Either `Install` or `Uninstall`. | | `action` | Either `Install` or `Uninstall`. |
| `status` | One of `Success`, `Failure`, `Pending`, or `Cancelled`. | | `status` | One of `Success`, `Failure`, `Pending`, or `Cancelled`. |
| `attribution` | Optionally defined by the user, associate the diagnostics of this run to the provided value. |
| `failure_chain` | A high level description of what the failure was, if any. For example: `Command("diskutil")` if the command `diskutil list` failed. | | `failure_chain` | A high level description of what the failure was, if any. For example: `Command("diskutil")` if the command `diskutil list` failed. |
To disable diagnostic reporting, set the diagnostics URL to an empty string by passing `--diagnostic-endpoint=""` or setting `NIX_INSTALLER_DIAGNOSTIC_ENDPOINT=""`. To disable diagnostic reporting, set the diagnostics URL to an empty string by passing `--diagnostic-endpoint=""` or setting `NIX_INSTALLER_DIAGNOSTIC_ENDPOINT=""`.

View file

@ -8,17 +8,15 @@
"rust-analyzer-src": "rust-analyzer-src" "rust-analyzer-src": "rust-analyzer-src"
}, },
"locked": { "locked": {
"lastModified": 1693549280,
"narHash": "sha256-Se0ceJDij5fJvucwTpuDZyuQOWnJfP/hX3XzTBuAzXg=", "narHash": "sha256-Se0ceJDij5fJvucwTpuDZyuQOWnJfP/hX3XzTBuAzXg=",
"owner": "nix-community",
"repo": "fenix",
"rev": "5b116a689c22ed2495c2b0f857539519a2951ce2", "rev": "5b116a689c22ed2495c2b0f857539519a2951ce2",
"type": "github" "revCount": 1584,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/nix-community/fenix/0.1.1584%2Brev-5b116a689c22ed2495c2b0f857539519a2951ce2/018a4fce-4088-7e9a-bbab-7a39da8de2ff/source.tar.gz"
}, },
"original": { "original": {
"owner": "nix-community", "type": "tarball",
"repo": "fenix", "url": "https://flakehub.com/f/nix-community/fenix/0.1.1584.tar.gz"
"type": "github"
} }
}, },
"flake-compat": { "flake-compat": {
@ -37,6 +35,22 @@
"type": "github" "type": "github"
} }
}, },
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"lowdown-src": { "lowdown-src": {
"flake": false, "flake": false,
"locked": { "locked": {
@ -75,23 +89,21 @@
}, },
"nix": { "nix": {
"inputs": { "inputs": {
"flake-compat": "flake-compat_2",
"lowdown-src": "lowdown-src", "lowdown-src": "lowdown-src",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"nixpkgs-regression": "nixpkgs-regression" "nixpkgs-regression": "nixpkgs-regression"
}, },
"locked": { "locked": {
"lastModified": 1674678482, "narHash": "sha256-QMYAkdtU+g9HlZKtoJ+AI6TbWzzovKGnPZJHfZdclc8=",
"narHash": "sha256-MtVatZVsV+dtjdD4AC4bztrnDFas+WZYHzQMt41FwzU=", "rev": "a212300a1d9f9c7b0daf19c00c87fc50480f54f4",
"owner": "nixos", "revCount": 14727,
"repo": "nix", "type": "tarball",
"rev": "435a16b5556f4171b4204a3f65c9dedf215f168c", "url": "https://api.flakehub.com/f/pinned/NixOS/nix/2.17.0/018a1daf-2c87-7730-8fc0-4885c5d8eff7/source.tar.gz"
"type": "github"
}, },
"original": { "original": {
"owner": "nixos", "type": "tarball",
"ref": "2.13.2", "url": "https://flakehub.com/f/NixOS/nix/2.17.0.tar.gz"
"repo": "nix",
"type": "github"
} }
}, },
"nixpkgs": { "nixpkgs": {
@ -128,18 +140,15 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1693500674, "narHash": "sha256-CXQ2MuledDVlVM5dLC4pB41cFlBWxRw4tCBsFrq3cRk=",
"narHash": "sha256-HDlg/j0Et+D8NWayNOsdvZrJ+nA4h3muXQxIMUlpDXo=", "rev": "970a59bd19eff3752ce552935687100c46e820a5",
"owner": "nixos", "revCount": 526521,
"repo": "nixpkgs", "type": "tarball",
"rev": "da938d190e2335209df6806ddcb982634e51918c", "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.526521%2Brev-970a59bd19eff3752ce552935687100c46e820a5/018aabed-d05c-7478-b3db-bed61902efec/source.tar.gz"
"type": "github"
}, },
"original": { "original": {
"owner": "nixos", "type": "tarball",
"ref": "nixpkgs-unstable", "url": "https://flakehub.com/f/NixOS/nixpkgs/0.1.0.tar.gz"
"repo": "nixpkgs",
"type": "github"
} }
}, },
"root": { "root": {

View file

@ -2,10 +2,10 @@
description = "The Determinate Nix Installer"; description = "The Determinate Nix Installer";
inputs = { inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.1.0.tar.gz";
fenix = { fenix = {
url = "github:nix-community/fenix"; url = "https://flakehub.com/f/nix-community/fenix/0.1.1584.tar.gz";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
@ -15,7 +15,7 @@
}; };
nix = { nix = {
url = "github:nixos/nix/2.13.2"; url = "https://flakehub.com/f/NixOS/nix/2.17.0.tar.gz";
# Omitting `inputs.nixpkgs.follows = "nixpkgs";` on purpose # Omitting `inputs.nixpkgs.follows = "nixpkgs";` on purpose
}; };

View file

@ -156,6 +156,7 @@ impl Planner for MyPlanner {
#[cfg(feature = "diagnostics")] #[cfg(feature = "diagnostics")]
async fn diagnostic_data(&self) -> Result<nix_installer::diagnostics::DiagnosticData, PlannerError> { async fn diagnostic_data(&self) -> Result<nix_installer::diagnostics::DiagnosticData, PlannerError> {
Ok(nix_installer::diagnostics::DiagnosticData::new( Ok(nix_installer::diagnostics::DiagnosticData::new(
self.common.diagnostic_attribution.clone(),
self.common.diagnostic_endpoint.clone(), self.common.diagnostic_endpoint.clone(),
self.typetag_name().into(), self.typetag_name().into(),
self.configured_settings() self.configured_settings()

View file

@ -34,6 +34,7 @@ pub enum DiagnosticAction {
/// A report sent to an endpoint /// A report sent to an endpoint
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct DiagnosticReport { pub struct DiagnosticReport {
pub attribution: Option<String>,
pub version: String, pub version: String,
pub planner: String, pub planner: String,
pub configured_settings: Vec<String>, pub configured_settings: Vec<String>,
@ -50,6 +51,7 @@ pub struct DiagnosticReport {
/// A preparation of data to be sent to the `endpoint`. /// A preparation of data to be sent to the `endpoint`.
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Default)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Default)]
pub struct DiagnosticData { pub struct DiagnosticData {
attribution: Option<String>,
version: String, version: String,
planner: String, planner: String,
configured_settings: Vec<String>, configured_settings: Vec<String>,
@ -65,6 +67,7 @@ pub struct DiagnosticData {
impl DiagnosticData { impl DiagnosticData {
pub fn new( pub fn new(
attribution: Option<String>,
endpoint: Option<String>, endpoint: Option<String>,
planner: String, planner: String,
configured_settings: Vec<String>, configured_settings: Vec<String>,
@ -81,6 +84,7 @@ impl DiagnosticData {
let is_ci = is_ci::cached() let is_ci = is_ci::cached()
|| std::env::var("NIX_INSTALLER_CI").unwrap_or_else(|_| "0".into()) == "1"; || std::env::var("NIX_INSTALLER_CI").unwrap_or_else(|_| "0".into()) == "1";
Ok(Self { Ok(Self {
attribution,
endpoint, endpoint,
version: env!("CARGO_PKG_VERSION").into(), version: env!("CARGO_PKG_VERSION").into(),
planner, planner,
@ -131,6 +135,7 @@ impl DiagnosticData {
pub fn report(&self, action: DiagnosticAction, status: DiagnosticStatus) -> DiagnosticReport { pub fn report(&self, action: DiagnosticAction, status: DiagnosticStatus) -> DiagnosticReport {
let Self { let Self {
attribution,
version, version,
planner, planner,
configured_settings, configured_settings,
@ -143,6 +148,7 @@ impl DiagnosticData {
failure_chain, failure_chain,
} = self; } = self;
DiagnosticReport { DiagnosticReport {
attribution: attribution.clone(),
version: version.clone(), version: version.clone(),
planner: planner.clone(), planner: planner.clone(),
configured_settings: configured_settings.clone(), configured_settings: configured_settings.clone(),

View file

@ -129,6 +129,7 @@ impl Planner for Linux {
#[cfg(feature = "diagnostics")] #[cfg(feature = "diagnostics")]
async fn diagnostic_data(&self) -> Result<crate::diagnostics::DiagnosticData, PlannerError> { async fn diagnostic_data(&self) -> Result<crate::diagnostics::DiagnosticData, PlannerError> {
Ok(crate::diagnostics::DiagnosticData::new( Ok(crate::diagnostics::DiagnosticData::new(
self.settings.diagnostic_attribution.clone(),
self.settings.diagnostic_endpoint.clone(), self.settings.diagnostic_endpoint.clone(),
self.typetag_name().into(), self.typetag_name().into(),
self.configured_settings() self.configured_settings()

View file

@ -210,6 +210,7 @@ impl Planner for Macos {
#[cfg(feature = "diagnostics")] #[cfg(feature = "diagnostics")]
async fn diagnostic_data(&self) -> Result<crate::diagnostics::DiagnosticData, PlannerError> { async fn diagnostic_data(&self) -> Result<crate::diagnostics::DiagnosticData, PlannerError> {
Ok(crate::diagnostics::DiagnosticData::new( Ok(crate::diagnostics::DiagnosticData::new(
self.settings.diagnostic_attribution.clone(),
self.settings.diagnostic_endpoint.clone(), self.settings.diagnostic_endpoint.clone(),
self.typetag_name().into(), self.typetag_name().into(),
self.configured_settings() self.configured_settings()

View file

@ -72,6 +72,7 @@ impl Planner for MyPlanner {
#[cfg(feature = "diagnostics")] #[cfg(feature = "diagnostics")]
async fn diagnostic_data(&self) -> Result<nix_installer::diagnostics::DiagnosticData, PlannerError> { async fn diagnostic_data(&self) -> Result<nix_installer::diagnostics::DiagnosticData, PlannerError> {
Ok(nix_installer::diagnostics::DiagnosticData::new( Ok(nix_installer::diagnostics::DiagnosticData::new(
self.common.diagnostic_attribution.clone(),
self.common.diagnostic_endpoint.clone(), self.common.diagnostic_endpoint.clone(),
self.typetag_name().into(), self.typetag_name().into(),
self.configured_settings() self.configured_settings()

View file

@ -262,6 +262,7 @@ impl Planner for Ostree {
#[cfg(feature = "diagnostics")] #[cfg(feature = "diagnostics")]
async fn diagnostic_data(&self) -> Result<crate::diagnostics::DiagnosticData, PlannerError> { async fn diagnostic_data(&self) -> Result<crate::diagnostics::DiagnosticData, PlannerError> {
Ok(crate::diagnostics::DiagnosticData::new( Ok(crate::diagnostics::DiagnosticData::new(
self.settings.diagnostic_attribution.clone(),
self.settings.diagnostic_endpoint.clone(), self.settings.diagnostic_endpoint.clone(),
self.typetag_name().into(), self.typetag_name().into(),
self.configured_settings() self.configured_settings()

View file

@ -388,6 +388,7 @@ impl Planner for SteamDeck {
#[cfg(feature = "diagnostics")] #[cfg(feature = "diagnostics")]
async fn diagnostic_data(&self) -> Result<crate::diagnostics::DiagnosticData, PlannerError> { async fn diagnostic_data(&self) -> Result<crate::diagnostics::DiagnosticData, PlannerError> {
Ok(crate::diagnostics::DiagnosticData::new( Ok(crate::diagnostics::DiagnosticData::new(
self.settings.diagnostic_attribution.clone(),
self.settings.diagnostic_endpoint.clone(), self.settings.diagnostic_endpoint.clone(),
self.typetag_name().into(), self.typetag_name().into(),
self.configured_settings() self.configured_settings()

View file

@ -205,12 +205,26 @@ pub struct CommonSettings {
)] )]
pub force: bool, pub force: bool,
#[cfg(feature = "diagnostics")]
/// Relate the install diagnostic to a specific value
#[cfg_attr(
feature = "cli",
clap(
long,
default_value = None,
env = "NIX_INSTALLER_DIAGNOSTIC_ATTRIBUTION",
global = true
)
)]
pub diagnostic_attribution: Option<String>,
#[cfg(feature = "diagnostics")] #[cfg(feature = "diagnostics")]
/// The URL or file path for an installation diagnostic to be sent /// The URL or file path for an installation diagnostic to be sent
/// ///
/// Sample of the data sent: /// Sample of the data sent:
/// ///
/// { /// {
/// "attribution": null,
/// "version": "0.4.0", /// "version": "0.4.0",
/// "planner": "linux", /// "planner": "linux",
/// "configured_settings": [ "modify_profile" ], /// "configured_settings": [ "modify_profile" ],
@ -301,6 +315,8 @@ impl CommonSettings {
force: false, force: false,
ssl_cert_file: Default::default(), ssl_cert_file: Default::default(),
#[cfg(feature = "diagnostics")] #[cfg(feature = "diagnostics")]
diagnostic_attribution: None,
#[cfg(feature = "diagnostics")]
diagnostic_endpoint: Some("https://install.determinate.systems/nix/diagnostic".into()), diagnostic_endpoint: Some("https://install.determinate.systems/nix/diagnostic".into()),
}) })
} }
@ -319,6 +335,8 @@ impl CommonSettings {
extra_conf, extra_conf,
force, force,
ssl_cert_file, ssl_cert_file,
#[cfg(feature = "diagnostics")]
diagnostic_attribution: _,
#[cfg(feature = "diagnostics")] #[cfg(feature = "diagnostics")]
diagnostic_endpoint, diagnostic_endpoint,
} = self; } = self;