diff --git a/docs/pages/docs/providers/deno.md b/docs/pages/docs/providers/deno.md index 51a0153ba..7badb4164 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 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 _None_ 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/deno1/src/index.ts b/examples/deno1/src/index.ts new file mode 100644 index 000000000..71d916996 --- /dev/null +++ b/examples/deno1/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/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..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.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..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}; +use super::{ + images::{DEBIAN_BASE_IMAGE, UBUNTU_BASE_IMAGE}, + nix::NIXPACKS_ARCHIVE_LEGACY_OPENSSL, +}; use crate::nixpacks::{ app::{App, StaticAssets}, environment::{Environment, EnvironmentVariables}, @@ -46,6 +49,8 @@ pub struct BuildPlan { pub phases: Option, + pub pinned_archive: Option, + #[serde(rename = "start")] pub start_phase: Option, } @@ -292,7 +297,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 +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(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 +498,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..b0dd46b98 100644 --- a/src/nixpacks/plan/phase.rs +++ b/src/nixpacks/plan/phase.rs @@ -1,6 +1,6 @@ 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 std::collections::{BTreeMap, HashSet}; @@ -209,10 +209,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 3deef7bee..e8e8fbb10 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, @@ -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 {} @@ -42,10 +54,28 @@ 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(); - let setup = Phase::setup(Some(vec![Pkg::new("deno")])); + let mut setup = Phase::setup(Some(vec![Pkg::new("deno")])); + + 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); if let Some(build_cmd) = DenoProvider::get_build_cmd(app)? { @@ -115,3 +145,41 @@ impl DenoProvider { Ok(Some(relative_path_to_index)) } } + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + 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/deno1").unwrap(), + &Environment::default() + ) + .unwrap() + .unwrap() + .phases + .unwrap() + .get("setup") + .unwrap() + .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" + } +} 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" + } +}