From bbaa0e463eae497197db75a24e8048309bec434e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20R=C5=ABti=C5=86=C5=A1?= Date: Mon, 16 Dec 2024 17:49:15 -0500 Subject: [PATCH 1/7] Use latest Deno 2 version --- src/nixpacks/nix/mod.rs | 3 +++ src/nixpacks/plan/generator.rs | 2 +- src/nixpacks/plan/mod.rs | 12 ++++++++---- src/nixpacks/plan/phase.rs | 7 ++++--- src/providers/deno.rs | 3 ++- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/nixpacks/nix/mod.rs b/src/nixpacks/nix/mod.rs index 4ca919109..9e00470ce 100644 --- a/src/nixpacks/nix/mod.rs +++ b/src/nixpacks/nix/mod.rs @@ -13,6 +13,9 @@ pub const NIXPKGS_ARCHIVE: &str = "5148520bfab61f99fd25fb9ff7bfbb50dad3c9db"; // Version of the Nix archive that uses OpenSSL 1.1 pub const NIXPACKS_ARCHIVE_LEGACY_OPENSSL: &str = "a0b7e70db7a55088d3de0cc370a59f9fbcc906c3"; +// Version of the Nix archive with the latest Deno +pub const NIXPACKS_ARCHIVE_LATEST_DENO: &str = "734af41a2b6a21fb9bf70d9f170563b6932364bb"; + /// Contains all the data needed to generate a Nix expression file for installing Nix dependencies. #[derive(Eq, PartialEq, Default, Debug, Clone)] struct NixGroup { diff --git a/src/nixpacks/plan/generator.rs b/src/nixpacks/plan/generator.rs index 98abe7df2..88fe94be4 100644 --- a/src/nixpacks/plan/generator.rs +++ b/src/nixpacks/plan/generator.rs @@ -80,7 +80,7 @@ impl NixpacksBuildPlanGenerator<'_> { plan.add_variables(Environment::clone_variables(new_env)); } - plan.pin(new_env.is_config_variable_truthy("DEBIAN")); + plan.pin(new_env.is_config_variable_truthy("DEBIAN"), plan.pinned_archive.clone()); if plan.clone().phases.unwrap_or_default().is_empty() { // try again in a subdir let dir_count = app.paths.clone().iter().filter(|p| p.is_dir()).count(); diff --git a/src/nixpacks/plan/mod.rs b/src/nixpacks/plan/mod.rs index 2b2db9e72..a49972656 100644 --- a/src/nixpacks/plan/mod.rs +++ b/src/nixpacks/plan/mod.rs @@ -3,7 +3,7 @@ use self::{ phase::{Phase, Phases, StartPhase}, topological_sort::topological_sort, }; -use super::images::{DEBIAN_BASE_IMAGE, UBUNTU_BASE_IMAGE}; +use super::{images::{DEBIAN_BASE_IMAGE, UBUNTU_BASE_IMAGE}, nix::NIXPACKS_ARCHIVE_LEGACY_OPENSSL}; use crate::nixpacks::{ app::{App, StaticAssets}, environment::{Environment, EnvironmentVariables}, @@ -45,6 +45,8 @@ pub struct BuildPlan { pub static_assets: Option, pub phases: Option, + + pub pinned_archive: Option, #[serde(rename = "start")] pub start_phase: Option, @@ -292,7 +294,7 @@ impl BuildPlan { } /// Store the base image and phase dependencies in this BuildPlan, for later reproducibility. - pub fn pin(&mut self, use_debian: bool) { + pub fn pin(&mut self, use_debian: bool, archive: Option) { self.providers = Some(Vec::new()); if self.build_image.is_none() { let base_image = if use_debian { @@ -306,12 +308,14 @@ impl BuildPlan { self.resolve_phase_names(); let phases = self.phases.get_or_insert(Phases::default()); for phase in (*phases).values_mut() { - phase.pin(use_debian); + phase.pin(if archive.is_some() { archive.clone() } else if use_debian { Some(NIXPACKS_ARCHIVE_LEGACY_OPENSSL.to_string()) } else { None }); } if let Some(start) = &mut self.start_phase { start.pin(); } + + self.pinned_archive = archive } /// Prefix each phase name with the name of the provider that generated the phase, in the case of multiple providers. @@ -485,7 +489,7 @@ mod test { ) .unwrap(); - plan.pin(false); + plan.pin(false, None); assert_eq!( plan.get_phase("setup").unwrap().nix_pkgs, Some(vec!["nodejs".to_string(), "yarn".to_string()]) diff --git a/src/nixpacks/plan/phase.rs b/src/nixpacks/plan/phase.rs index d64113d41..0d74baa03 100644 --- a/src/nixpacks/plan/phase.rs +++ b/src/nixpacks/plan/phase.rs @@ -3,6 +3,7 @@ use crate::nixpacks::{ nix::{pkg::Pkg, NIXPACKS_ARCHIVE_LEGACY_OPENSSL, NIXPKGS_ARCHIVE}, }; use serde::{Deserialize, Serialize}; +use core::arch; use std::collections::{BTreeMap, HashSet}; use std::hash::Hash; @@ -209,10 +210,10 @@ impl Phase { } /// Store the phase dependencies for later reproducibility. - pub fn pin(&mut self, use_legacy_openssl: bool) { + pub fn pin(&mut self, archive: Option) { if self.uses_nix() && self.nixpkgs_archive.is_none() { - self.nixpkgs_archive = if use_legacy_openssl { - Some(NIXPACKS_ARCHIVE_LEGACY_OPENSSL.to_string()) + self.nixpkgs_archive = if let Some(archive) = archive { + Some(archive) } else { Some(NIXPKGS_ARCHIVE.to_string()) } diff --git a/src/providers/deno.rs b/src/providers/deno.rs index a5289ec46..50d99a906 100644 --- a/src/providers/deno.rs +++ b/src/providers/deno.rs @@ -4,7 +4,7 @@ use super::Provider; use crate::nixpacks::{ app::App, environment::Environment, - nix::pkg::Pkg, + nix::{pkg::Pkg, NIXPACKS_ARCHIVE_LATEST_DENO}, plan::{ phase::{Phase, StartPhase}, BuildPlan, @@ -44,6 +44,7 @@ impl Provider for DenoProvider { fn get_build_plan(&self, app: &App, _env: &Environment) -> Result> { let mut plan = BuildPlan::default(); + plan.pin(false, Some(NIXPACKS_ARCHIVE_LATEST_DENO.to_string())); let setup = Phase::setup(Some(vec![Pkg::new("deno")])); plan.add_phase(setup); From 44d9c2ca255a84bcfb97473e3630c718855f524e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20R=C5=ABti=C5=86=C5=A1?= Date: Mon, 16 Dec 2024 17:53:40 -0500 Subject: [PATCH 2/7] clippy + fmt --- src/nixpacks/plan/generator.rs | 5 ++++- src/nixpacks/plan/mod.rs | 17 +++++++++++++---- src/nixpacks/plan/phase.rs | 3 +-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/nixpacks/plan/generator.rs b/src/nixpacks/plan/generator.rs index 88fe94be4..28f8465e2 100644 --- a/src/nixpacks/plan/generator.rs +++ b/src/nixpacks/plan/generator.rs @@ -80,7 +80,10 @@ impl NixpacksBuildPlanGenerator<'_> { plan.add_variables(Environment::clone_variables(new_env)); } - plan.pin(new_env.is_config_variable_truthy("DEBIAN"), plan.pinned_archive.clone()); + plan.pin( + new_env.is_config_variable_truthy("DEBIAN"), + plan.pinned_archive.clone(), + ); if plan.clone().phases.unwrap_or_default().is_empty() { // try again in a subdir let dir_count = app.paths.clone().iter().filter(|p| p.is_dir()).count(); diff --git a/src/nixpacks/plan/mod.rs b/src/nixpacks/plan/mod.rs index a49972656..9fa709db2 100644 --- a/src/nixpacks/plan/mod.rs +++ b/src/nixpacks/plan/mod.rs @@ -3,7 +3,10 @@ use self::{ phase::{Phase, Phases, StartPhase}, topological_sort::topological_sort, }; -use super::{images::{DEBIAN_BASE_IMAGE, UBUNTU_BASE_IMAGE}, nix::NIXPACKS_ARCHIVE_LEGACY_OPENSSL}; +use super::{ + images::{DEBIAN_BASE_IMAGE, UBUNTU_BASE_IMAGE}, + nix::NIXPACKS_ARCHIVE_LEGACY_OPENSSL, +}; use crate::nixpacks::{ app::{App, StaticAssets}, environment::{Environment, EnvironmentVariables}, @@ -45,7 +48,7 @@ pub struct BuildPlan { pub static_assets: Option, pub phases: Option, - + pub pinned_archive: Option, #[serde(rename = "start")] @@ -308,14 +311,20 @@ impl BuildPlan { self.resolve_phase_names(); let phases = self.phases.get_or_insert(Phases::default()); for phase in (*phases).values_mut() { - phase.pin(if archive.is_some() { archive.clone() } else if use_debian { Some(NIXPACKS_ARCHIVE_LEGACY_OPENSSL.to_string()) } else { None }); + phase.pin(if archive.is_some() { + archive.clone() + } else if use_debian { + Some(NIXPACKS_ARCHIVE_LEGACY_OPENSSL.to_string()) + } else { + None + }); } if let Some(start) = &mut self.start_phase { start.pin(); } - self.pinned_archive = archive + self.pinned_archive = archive; } /// Prefix each phase name with the name of the provider that generated the phase, in the case of multiple providers. diff --git a/src/nixpacks/plan/phase.rs b/src/nixpacks/plan/phase.rs index 0d74baa03..b0dd46b98 100644 --- a/src/nixpacks/plan/phase.rs +++ b/src/nixpacks/plan/phase.rs @@ -1,9 +1,8 @@ use crate::nixpacks::{ images::{DEFAULT_BASE_IMAGE, STANDALONE_IMAGE}, - nix::{pkg::Pkg, NIXPACKS_ARCHIVE_LEGACY_OPENSSL, NIXPKGS_ARCHIVE}, + nix::{pkg::Pkg, NIXPKGS_ARCHIVE}, }; use serde::{Deserialize, Serialize}; -use core::arch; use std::collections::{BTreeMap, HashSet}; use std::hash::Hash; From 1f941de6819d441d9980382ca872789390850f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20R=C5=ABti=C5=86=C5=A1?= Date: Mon, 16 Dec 2024 20:40:58 -0500 Subject: [PATCH 3/7] Add example, put deno 2 behind a flag --- examples/deno2/nixpacks.toml | 2 ++ examples/deno2/src/index.ts | 7 +++++++ src/providers/deno.rs | 8 +++++--- 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 examples/deno2/nixpacks.toml create mode 100644 examples/deno2/src/index.ts diff --git a/examples/deno2/nixpacks.toml b/examples/deno2/nixpacks.toml new file mode 100644 index 000000000..b497c2708 --- /dev/null +++ b/examples/deno2/nixpacks.toml @@ -0,0 +1,2 @@ +[variables] +NIXPACKS_USE_DENO_2 = '1' \ No newline at end of file diff --git a/examples/deno2/src/index.ts b/examples/deno2/src/index.ts new file mode 100644 index 000000000..71d916996 --- /dev/null +++ b/examples/deno2/src/index.ts @@ -0,0 +1,7 @@ +import * as o from "https://deno.land/x/cowsay/mod.ts"; + +let m = o.say({ + text: "Hello from Deno", +}); + +console.log(m); diff --git a/src/providers/deno.rs b/src/providers/deno.rs index 50d99a906..a4ad3d918 100644 --- a/src/providers/deno.rs +++ b/src/providers/deno.rs @@ -42,11 +42,13 @@ impl Provider for DenoProvider { || app.find_match(&re, "**/*.{ts,tsx,js,jsx}")?) } - fn get_build_plan(&self, app: &App, _env: &Environment) -> Result> { + fn get_build_plan(&self, app: &App, env: &Environment) -> Result> { let mut plan = BuildPlan::default(); - plan.pin(false, Some(NIXPACKS_ARCHIVE_LATEST_DENO.to_string())); - let setup = Phase::setup(Some(vec![Pkg::new("deno")])); + let mut setup = Phase::setup(Some(vec![Pkg::new("deno")])); + if env.is_config_variable_truthy("USE_DENO_2") { + setup.pin(Some(NIXPACKS_ARCHIVE_LATEST_DENO.to_string())); + } plan.add_phase(setup); if let Some(build_cmd) = DenoProvider::get_build_cmd(app)? { From 5245be840364b72f36d73b4771fab31a4c570518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20R=C5=ABti=C5=86=C5=A1?= Date: Mon, 16 Dec 2024 20:58:02 -0500 Subject: [PATCH 4/7] Proper tests --- src/providers/deno.rs | 26 ++++++++++++++ .../snapshots/generate_plan_tests__deno2.snap | 36 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 tests/snapshots/generate_plan_tests__deno2.snap diff --git a/src/providers/deno.rs b/src/providers/deno.rs index a4ad3d918..1e370767d 100644 --- a/src/providers/deno.rs +++ b/src/providers/deno.rs @@ -118,3 +118,29 @@ impl DenoProvider { Ok(Some(relative_path_to_index)) } } + +mod tests { + use crate::nixpacks::nix::NIXPACKS_ARCHIVE_LATEST_DENO; + use crate::{App, DenoProvider, Environment, Provider}; + + #[test] + fn test_deno2() { + let deno = DenoProvider {}; + assert_eq!( + deno.get_build_plan( + &App::new("examples/deno2").unwrap(), + &Environment::from_envs(vec!["NIXPACKS_USE_DENO_2=1"]).unwrap() + ) + .unwrap() + .unwrap() + .phases + .unwrap() + .get("setup") + .unwrap() + .nixpkgs_archive + .as_ref() + .unwrap(), + &NIXPACKS_ARCHIVE_LATEST_DENO.to_string() + ); + } +} diff --git a/tests/snapshots/generate_plan_tests__deno2.snap b/tests/snapshots/generate_plan_tests__deno2.snap new file mode 100644 index 000000000..e60c96a49 --- /dev/null +++ b/tests/snapshots/generate_plan_tests__deno2.snap @@ -0,0 +1,36 @@ +--- +source: tests/generate_plan_tests.rs +expression: plan +snapshot_kind: text +--- +{ + "providers": [], + "buildImage": "[build_image]", + "variables": { + "NIXPACKS_METADATA": "deno", + "NIXPACKS_USE_DENO_2": "1" + }, + "phases": { + "build": { + "name": "build", + "dependsOn": [ + "install", + "setup" + ], + "cmds": [ + "deno cache src/index.ts" + ] + }, + "setup": { + "name": "setup", + "nixPkgs": [ + "deno" + ], + "nixOverlays": [], + "nixpkgsArchive": "[archive]" + } + }, + "start": { + "cmd": "deno run --allow-all src/index.ts" + } +} From 0a7e484dd50eddc1a0b51aaddc5817bba1da8311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20R=C5=ABti=C5=86=C5=A1?= Date: Mon, 16 Dec 2024 20:59:01 -0500 Subject: [PATCH 5/7] Update docs for deno 2 --- docs/pages/docs/providers/deno.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/docs/providers/deno.md b/docs/pages/docs/providers/deno.md index 51a0153ba..e8e8d978d 100644 --- a/docs/pages/docs/providers/deno.md +++ b/docs/pages/docs/providers/deno.md @@ -8,6 +8,8 @@ Deno is detected if there is a `deno.{json,jsonc}` file found or if any `.{ts,ts Apps built with [Deno Fresh](https://fresh.deno.dev/) should work out of the box. +Deno 2 will be installed if the `NIXPACKS_USE_DENO_2` environment variable is truthy; otherwise, Deno 1 will be used. + ## Install _None_ From bfef7f42d851de638194c35bafb623ae7e6a442e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20R=C5=ABti=C5=86=C5=A1?= Date: Mon, 16 Dec 2024 21:02:04 -0500 Subject: [PATCH 6/7] fmt + clippy --- src/providers/deno.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/providers/deno.rs b/src/providers/deno.rs index 1e370767d..4a2d587ba 100644 --- a/src/providers/deno.rs +++ b/src/providers/deno.rs @@ -119,9 +119,9 @@ impl DenoProvider { } } +#[cfg(test)] mod tests { - use crate::nixpacks::nix::NIXPACKS_ARCHIVE_LATEST_DENO; - use crate::{App, DenoProvider, Environment, Provider}; + use super::*; #[test] fn test_deno2() { From fee967a07ddb969f223f27521e5e2c95fefff9eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20R=C5=ABti=C5=86=C5=A1?= Date: Fri, 20 Dec 2024 14:36:45 -0500 Subject: [PATCH 7/7] Fully support Deno 2 by default, support `engines.deno` --- docs/pages/docs/providers/deno.md | 2 +- examples/deno1/deno.json | 5 ++ examples/{deno2 => deno1}/src/index.ts | 0 examples/deno2/nixpacks.toml | 2 - src/providers/deno.rs | 55 ++++++++++++++++--- .../snapshots/generate_plan_tests__deno1.snap | 35 ++++++++++++ 6 files changed, 88 insertions(+), 11 deletions(-) create mode 100644 examples/deno1/deno.json rename examples/{deno2 => deno1}/src/index.ts (100%) delete mode 100644 examples/deno2/nixpacks.toml create mode 100644 tests/snapshots/generate_plan_tests__deno1.snap diff --git a/docs/pages/docs/providers/deno.md b/docs/pages/docs/providers/deno.md index e8e8d978d..7badb4164 100644 --- a/docs/pages/docs/providers/deno.md +++ b/docs/pages/docs/providers/deno.md @@ -8,7 +8,7 @@ Deno is detected if there is a `deno.{json,jsonc}` file found or if any `.{ts,ts Apps built with [Deno Fresh](https://fresh.deno.dev/) should work out of the box. -Deno 2 will be installed if the `NIXPACKS_USE_DENO_2` environment variable is truthy; otherwise, Deno 1 will be used. +Deno 1 will be installed if the `NIXPACKS_USE_DENO_1` environment variable is truthy or `engines.deno` is set to some variant of `1` (e.g. `1`, `v1`, `^1.0`) in either `package.json` or `deno.json`; otherwise, Deno 2 will be used. ## Install diff --git a/examples/deno1/deno.json b/examples/deno1/deno.json new file mode 100644 index 000000000..d71d5d793 --- /dev/null +++ b/examples/deno1/deno.json @@ -0,0 +1,5 @@ +{ + "engines": { + "deno": "1" + } +} diff --git a/examples/deno2/src/index.ts b/examples/deno1/src/index.ts similarity index 100% rename from examples/deno2/src/index.ts rename to examples/deno1/src/index.ts diff --git a/examples/deno2/nixpacks.toml b/examples/deno2/nixpacks.toml deleted file mode 100644 index b497c2708..000000000 --- a/examples/deno2/nixpacks.toml +++ /dev/null @@ -1,2 +0,0 @@ -[variables] -NIXPACKS_USE_DENO_2 = '1' \ No newline at end of file diff --git a/src/providers/deno.rs b/src/providers/deno.rs index 4a2d587ba..442506171 100644 --- a/src/providers/deno.rs +++ b/src/providers/deno.rs @@ -20,9 +20,21 @@ pub struct DenoTasks { pub start: Option, } +#[derive(Serialize, Deserialize, Default, Debug)] +pub struct DenoEngines { + pub deno: Option, +} + #[derive(Serialize, Deserialize, Default, Debug)] pub struct DenoJson { pub tasks: Option, + pub engines: Option, +} + +#[derive(Serialize, Deserialize, Default, Debug)] +pub struct PackageJson { + pub engines: Option, + pub scripts: Option, } pub struct DenoProvider {} @@ -46,7 +58,22 @@ impl Provider for DenoProvider { let mut plan = BuildPlan::default(); let mut setup = Phase::setup(Some(vec![Pkg::new("deno")])); - if env.is_config_variable_truthy("USE_DENO_2") { + + let package_json = app.read_json::("package.json"); + let deno_json = app.read_json::("deno.json"); + let v1_regex = Regex::new(r"^((>=)|\^)?v?1")?; + if !(env.is_config_variable_truthy("USE_DENO_1") + || package_json + .map(|p| p.engines.map(|e| e.deno.map(|d| v1_regex.is_match(&d)))) + .unwrap_or(Some(Some(false))) + .unwrap_or(Some(false)) + .unwrap_or(false) + || deno_json + .map(|p| p.engines.map(|e| e.deno.map(|d| v1_regex.is_match(&d)))) + .unwrap_or(Some(Some(false))) + .unwrap_or(Some(false)) + .unwrap_or(false)) + { setup.pin(Some(NIXPACKS_ARCHIVE_LATEST_DENO.to_string())); } plan.add_phase(setup); @@ -121,15 +148,29 @@ impl DenoProvider { #[cfg(test)] mod tests { + use super::*; #[test] - fn test_deno2() { + fn test_deno_versions() { let deno = DenoProvider {}; + assert_eq!( + deno.get_build_plan(&App::new("examples/deno").unwrap(), &Environment::default()) + .unwrap() + .unwrap() + .phases + .unwrap() + .get("setup") + .unwrap() + .nixpkgs_archive + .as_ref() + .unwrap(), + &NIXPACKS_ARCHIVE_LATEST_DENO.to_string() + ); assert_eq!( deno.get_build_plan( - &App::new("examples/deno2").unwrap(), - &Environment::from_envs(vec!["NIXPACKS_USE_DENO_2=1"]).unwrap() + &App::new("examples/deno1").unwrap(), + &Environment::default() ) .unwrap() .unwrap() @@ -137,10 +178,8 @@ mod tests { .unwrap() .get("setup") .unwrap() - .nixpkgs_archive - .as_ref() - .unwrap(), - &NIXPACKS_ARCHIVE_LATEST_DENO.to_string() + .nixpkgs_archive, + None ); } } diff --git a/tests/snapshots/generate_plan_tests__deno1.snap b/tests/snapshots/generate_plan_tests__deno1.snap new file mode 100644 index 000000000..08f32a3ea --- /dev/null +++ b/tests/snapshots/generate_plan_tests__deno1.snap @@ -0,0 +1,35 @@ +--- +source: tests/generate_plan_tests.rs +expression: plan +snapshot_kind: text +--- +{ + "providers": [], + "buildImage": "[build_image]", + "variables": { + "NIXPACKS_METADATA": "deno" + }, + "phases": { + "build": { + "name": "build", + "dependsOn": [ + "install", + "setup" + ], + "cmds": [ + "deno cache src/index.ts" + ] + }, + "setup": { + "name": "setup", + "nixPkgs": [ + "deno" + ], + "nixOverlays": [], + "nixpkgsArchive": "[archive]" + } + }, + "start": { + "cmd": "deno run --allow-all src/index.ts" + } +}