deno_core is an optional feature flag (#4473)

* full

* all

* add deno_core as features

* all

* remove warnings

* all
This commit is contained in:
Ruben Fiszel
2024-10-03 13:11:58 +02:00
committed by GitHub
parent 6e81c680a9
commit d0b24d07d2
14 changed files with 326 additions and 196 deletions
+2
View File
@@ -43,6 +43,8 @@ RUN wget https://golang.org/dl/go1.21.5.linux-amd64.tar.gz && tar -C /usr/local
ENV PATH="${PATH}:/usr/local/go/bin"
ENV GO_PATH=/usr/local/go/bin/go
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
ENV TZ=Etc/UTC
ENV PYTHON_VERSION 3.11.4
+2 -2
View File
@@ -44,5 +44,5 @@ jobs:
mkdir frontend/build && cd backend && touch
windmill-api/openapi-deref.yaml &&
DATABASE_URL=postgres://postgres:changeme@postgres:5432/windmill
DISABLE_EMBEDDING=true RUST_LOG=info cargo test --features enterprise
--all -- --nocapture
DISABLE_EMBEDDING=true RUST_LOG=info cargo test --features
enterprise,deno_core --all -- --nocapture
+1 -1
View File
@@ -62,7 +62,7 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core
tags: |
${{ steps.meta-ee-public.outputs.tags }}
labels: |
+1 -1
View File
@@ -67,7 +67,7 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
build-args: |
features=embedding,parquet,openidconnect
features=embedding,parquet,openidconnect,deno_core
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
${{ steps.meta-public.outputs.tags }}
+7 -5
View File
@@ -1,8 +1,10 @@
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.event_name != 'pull_request' && github.repository ||
IMAGE_NAME:
${{ github.event_name != 'pull_request' && github.repository ||
'windmill-labs/windmill-test' }}
DEV_SHA: ${{ github.event_name != 'pull_request' && 'dev' || format('pr-{0}',
DEV_SHA:
${{ github.event_name != 'pull_request' && 'dev' || format('pr-{0}',
github.event.number) }}
name: Build windmill:main
@@ -75,7 +77,7 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
build-args: |
features=embedding,parquet,openidconnect,jemalloc
features=embedding,parquet,openidconnect,jemalloc,deno_core
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }}
${{ steps.meta-public.outputs.tags }}
@@ -136,7 +138,7 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
${{ steps.meta-ee-public.outputs.tags }}
@@ -198,7 +200,7 @@ jobs:
platforms: linux/amd64
push: true
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core
PYTHON_IMAGE=python:3.12.2-slim-bookworm
tags: |
${{ steps.meta-ee-public-py312.outputs.tags }}
+1
View File
@@ -10919,6 +10919,7 @@ dependencies = [
"serde_json",
"sha2 0.10.8",
"sqlx",
"swc_ecma_parser 0.144.3",
"tar",
"tiberius",
"tokio",
+3 -1
View File
@@ -57,6 +57,7 @@ cloud = ["windmill-queue/cloud", "windmill-worker/cloud"]
jemalloc = ["windmill-common/jemalloc", "dep:tikv-jemallocator", "dep:tikv-jemalloc-sys", "dep:tikv-jemalloc-ctl"]
tantivy = ["dep:windmill-indexer", "windmill-api/tantivy"]
sqlx = ["windmill-worker/sqlx"]
deno_core = ["windmill-worker/deno_core", "dep:deno_core"]
[dependencies]
anyhow.workspace = true
@@ -85,7 +86,7 @@ uuid.workspace = true
gethostname.workspace = true
serde_json.workspace = true
serde.workspace = true
deno_core.workspace = true
deno_core = { workspace = true, optional = true }
object_store = { workspace = true, optional = true }
pg-embed = {git = "https://github.com/faokunega/pg-embed", optional = true, default-features = false, features = ['rt_tokio']}
quote.workspace = true
@@ -105,6 +106,7 @@ serde.workspace = true
windmill-api-client.workspace = true
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting", "unsafe_use_unprotected_platform"] }
[workspace.dependencies]
windmill-api = { path = "./windmill-api", default-features = false }
windmill-queue = { path = "./windmill-queue" }
+1
View File
@@ -118,6 +118,7 @@ where
}
pub fn main() -> anyhow::Result<()> {
#[cfg(feature = "deno_core")]
deno_core::JsRuntime::init_platform(None);
create_and_run_current_thread_inner(windmill_main())
}
+21 -16
View File
@@ -18,6 +18,7 @@ parquet = ["windmill-common/parquet", "dep:object_store"]
flow_testing = []
cloud = []
sqlx = []
deno_core = ["dep:deno_fetch", "dep:deno_webidl", "dep:deno_web", "dep:deno_net", "dep:deno_console", "dep:deno_url", "dep:deno_core", "dep:deno_ast", "dep:deno_tls"]
[dependencies]
windmill-queue.workspace = true
@@ -59,15 +60,15 @@ once_cell.workspace = true
rsmq_async.workspace = true
tokio-postgres.workspace = true
bit-vec.workspace = true
deno_fetch.workspace = true
deno_webidl.workspace = true
deno_web.workspace = true
deno_net.workspace = true
deno_console.workspace = true
deno_url.workspace = true
deno_core.workspace = true
deno_ast.workspace = true
deno_tls.workspace = true
deno_fetch = { workspace = true, optional = true }
deno_webidl = { workspace = true, optional = true }
deno_web = { workspace = true, optional = true }
deno_net = { workspace = true, optional = true }
deno_console = { workspace = true, optional = true }
deno_url = { workspace = true, optional = true }
deno_core = { workspace = true, optional = true }
deno_ast = { workspace = true, optional = true }
deno_tls = { workspace = true, optional = true }
postgres-native-tls.workspace = true
native-tls.workspace = true
mysql_async.workspace = true
@@ -89,13 +90,17 @@ tar.workspace = true
object_store = { workspace = true, optional = true}
convert_case.workspace = true
yaml-rust.workspace = true
swc_ecma_parser.workspace = true
[build-dependencies]
deno_fetch.workspace = true
deno_webidl.workspace = true
deno_web.workspace = true
deno_console.workspace = true
deno_url.workspace = true
deno_core.workspace = true
deno_net.workspace = true
deno_fetch = { workspace = true, optional = true }
deno_webidl = { workspace = true, optional = true }
deno_web = { workspace = true, optional = true }
deno_net = { workspace = true, optional = true }
deno_console = { workspace = true, optional = true }
deno_url = { workspace = true, optional = true }
deno_core = { workspace = true, optional = true }
deno_ast = { workspace = true, optional = true }
deno_tls = { workspace = true, optional = true }
zstd.workspace = true
+16
View File
@@ -1,13 +1,22 @@
#[cfg(feature = "deno_core")]
use deno_fetch::FetchPermissions;
#[cfg(feature = "deno_core")]
use deno_net::NetPermissions;
#[cfg(feature = "deno_core")]
use deno_web::{BlobStore, TimersPermission};
#[cfg(feature = "deno_core")]
use std::env;
#[cfg(feature = "deno_core")]
use std::io::Write;
#[cfg(feature = "deno_core")]
use std::path::PathBuf;
#[cfg(feature = "deno_core")]
use std::sync::Arc;
#[cfg(feature = "deno_core")]
pub struct PermissionsContainer;
#[cfg(feature = "deno_core")]
impl FetchPermissions for PermissionsContainer {
#[inline(always)]
fn check_net_url(
@@ -28,6 +37,7 @@ impl FetchPermissions for PermissionsContainer {
}
}
#[cfg(feature = "deno_core")]
impl TimersPermission for PermissionsContainer {
#[inline(always)]
fn allow_hrtime(&mut self) -> bool {
@@ -35,6 +45,7 @@ impl TimersPermission for PermissionsContainer {
}
}
#[cfg(feature = "deno_core")]
impl NetPermissions for PermissionsContainer {
fn check_read(
&mut self,
@@ -61,12 +72,14 @@ impl NetPermissions for PermissionsContainer {
}
}
#[cfg(feature = "deno_core")]
deno_core::extension!(
fetch,
esm_entry_point = "ext:fetch/src/runtime.js",
esm = ["src/runtime.js"],
);
#[cfg(feature = "deno_core")]
fn main() {
println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap());
println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap());
@@ -122,3 +135,6 @@ fn main() {
println!("cargo:rerun-if-changed={}", path.display());
}
}
#[cfg(not(feature = "deno_core"))]
fn main() {}
+53 -40
View File
@@ -1,8 +1,14 @@
use std::{collections::HashMap, fs, io, path::Path, process::Stdio, time::Instant};
#[cfg(feature = "deno_core")]
use std::time::Instant;
use std::{collections::HashMap, fs, io, path::Path, process::Stdio};
use base64::Engine;
use itertools::Itertools;
#[cfg(not(feature = "deno_core"))]
use serde_json::value::to_raw_value;
use serde_json::value::RawValue;
use sha2::Digest;
use uuid::Uuid;
use windmill_parser_ts::remove_pinned_imports;
@@ -1190,51 +1196,58 @@ try {{
}
}
if annotation.native_mode {
let env_code = format!(
#[cfg(not(feature = "deno_core"))]
return Ok(to_raw_value("").unwrap());
#[cfg(feature = "deno_core")]
{
let env_code = format!(
"const process = {{ env: {{}} }};\nconst BASE_URL = '{base_internal_url}';\nconst BASE_INTERNAL_URL = '{base_internal_url}';\nprocess.env['BASE_URL'] = BASE_URL;process.env['BASE_INTERNAL_URL'] = BASE_INTERNAL_URL;\n{}",
reserved_variables
.iter()
.map(|(k, v)| format!("process.env['{}'] = '{}';\n", k, v))
.collect::<Vec<String>>()
.join("\n"));
let js_code = read_file_content(&format!("{job_dir}/main.js")).await?;
let started_at = Instant::now();
let args = crate::common::build_args_map(job, client, db)
.await?
.map(sqlx::types::Json);
let job_args = if args.is_some() {
args.as_ref()
} else {
job.args.as_ref()
};
let result = crate::js_eval::eval_fetch_timeout(
env_code,
inner_content.clone(),
js_code,
job_args,
job.id,
job.timeout,
db,
mem_peak,
canceled_by,
worker_name,
&job.workspace_id,
false,
occupancy_metrics,
)
.await?;
tracing::info!(
"Executed native code in {}ms",
started_at.elapsed().as_millis()
);
append_logs(
&job.id,
&job.workspace_id,
format!("{}\n{}", init_logs, result.1),
db,
)
.await;
return Ok(result.0);
let js_code = read_file_content(&format!("{job_dir}/main.js")).await?;
let started_at = Instant::now();
let args = crate::common::build_args_map(job, client, db)
.await?
.map(sqlx::types::Json);
let job_args = if args.is_some() {
args.as_ref()
} else {
job.args.as_ref()
};
let result = crate::js_eval::eval_fetch_timeout(
env_code,
inner_content.clone(),
js_code,
job_args,
job.id,
job.timeout,
db,
mem_peak,
canceled_by,
worker_name,
&job.workspace_id,
false,
occupancy_metrics,
)
.await?;
tracing::info!(
"Executed native code in {}ms",
started_at.elapsed().as_millis()
);
append_logs(
&job.id,
&job.workspace_id,
format!("{}\n{}", init_logs, result.1),
db,
)
.await;
return Ok(result.0);
}
}
append_logs(&job.id, &job.workspace_id, init_logs, db).await;
+1 -1
View File
@@ -1,5 +1,5 @@
use deno_ast::swc::parser::lexer::util::CharExt;
use itertools::Itertools;
use swc_ecma_parser::lexer::util::CharExt;
#[cfg(all(feature = "enterprise", feature = "parquet"))]
use object_store::path::Path;
+193 -114
View File
@@ -6,56 +6,72 @@
* LICENSE-AGPL for a copy of the license.
*/
#[cfg(feature = "deno_core")]
use std::{
cell::RefCell,
collections::HashMap,
env,
io::{self, BufReader},
rc::Rc,
sync::Arc,
};
use std::{collections::HashMap, sync::Arc};
#[cfg(feature = "deno_core")]
use deno_ast::ParseParams;
#[cfg(feature = "deno_core")]
use deno_core::{
error::AnyError,
op2, serde_v8, url,
v8::{self, IsolateHandle},
Extension, JsRuntime, OpState, PollEventLoopOptions, RuntimeOptions,
};
#[cfg(feature = "deno_core")]
use deno_fetch::FetchPermissions;
#[cfg(feature = "deno_core")]
use deno_net::NetPermissions;
#[cfg(feature = "deno_core")]
use deno_tls::{rustls::RootCertStore, rustls_pemfile};
#[cfg(feature = "deno_core")]
use deno_web::{BlobStore, TimersPermission};
#[cfg(feature = "deno_core")]
use itertools::Itertools;
use lazy_static::lazy_static;
use regex::Regex;
use serde_json::value::RawValue;
use sqlx::types::Json;
#[cfg(feature = "deno_core")]
use tokio::{
sync::{mpsc, oneshot},
time::timeout,
};
use uuid::Uuid;
use windmill_common::{error::Error, flow_status::JobResult, DB};
#[cfg(feature = "deno_core")]
use windmill_common::error::Error;
use windmill_common::{flow_status::JobResult, DB};
use windmill_queue::CanceledBy;
use crate::{
common::{unsafe_raw, OccupancyMetrics},
handle_child::run_future_with_polling_update_job_poller,
AuthedClient,
};
use crate::{common::OccupancyMetrics, AuthedClient};
#[cfg(feature = "deno_core")]
use crate::{common::unsafe_raw, handle_child::run_future_with_polling_update_job_poller};
#[derive(Debug, Clone)]
pub struct IdContext {
pub flow_job: Uuid,
#[allow(dead_code)]
pub steps_results: HashMap<String, JobResult>,
pub previous_id: String,
}
#[cfg(feature = "deno_core")]
pub struct ContainerRootCertStoreProvider {
root_cert_store: RootCertStore,
}
#[cfg(feature = "deno_core")]
impl ContainerRootCertStoreProvider {
fn new() -> ContainerRootCertStoreProvider {
return ContainerRootCertStoreProvider {
@@ -73,14 +89,17 @@ impl ContainerRootCertStoreProvider {
}
}
#[cfg(feature = "deno_core")]
impl deno_tls::RootCertStoreProvider for ContainerRootCertStoreProvider {
fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError> {
Ok(&self.root_cert_store)
}
}
#[cfg(feature = "deno_core")]
pub struct PermissionsContainer;
#[cfg(feature = "deno_core")]
impl FetchPermissions for PermissionsContainer {
#[inline(always)]
fn check_net_url(
@@ -101,6 +120,7 @@ impl FetchPermissions for PermissionsContainer {
}
}
#[cfg(feature = "deno_core")]
impl TimersPermission for PermissionsContainer {
#[inline(always)]
fn allow_hrtime(&mut self) -> bool {
@@ -108,6 +128,7 @@ impl TimersPermission for PermissionsContainer {
}
}
#[cfg(feature = "deno_core")]
impl NetPermissions for PermissionsContainer {
fn check_read(
&mut self,
@@ -134,6 +155,7 @@ impl NetPermissions for PermissionsContainer {
}
}
#[cfg(feature = "deno_core")]
pub struct OptAuthedClient(Option<AuthedClient>);
pub async fn eval_timeout(
@@ -142,7 +164,7 @@ pub async fn eval_timeout(
flow_input: Option<mappable_rc::Marc<HashMap<String, Box<RawValue>>>>,
authed_client: Option<&AuthedClient>,
by_id: Option<IdContext>,
ctx: Option<Vec<(String, String)>>,
#[allow(unused_variables)] ctx: Option<Vec<(String, String)>>,
) -> anyhow::Result<Box<RawValue>> {
let expr = expr.trim().to_string();
@@ -212,121 +234,133 @@ pub async fn eval_timeout(
}
}
let expr2 = expr.clone();
let (sender, mut receiver) = oneshot::channel::<IsolateHandle>();
let has_client = authed_client.is_some();
let authed_client = authed_client.cloned();
timeout(
std::time::Duration::from_millis(10000),
tokio::task::spawn_blocking(move || {
let mut ops = vec![op_get_context()];
#[cfg(not(feature = "deno_core"))]
{
#[allow(unreachable_code)]
return todo!();
}
if authed_client.is_some() {
ops.extend([
// An op for summing an array of numbers
// The op-layer automatically deserializes inputs
// and serializes the returned Result & value
op_variable(),
op_resource(),
])
}
#[cfg(feature = "deno_core")]
{
let expr2 = expr.clone();
let (sender, mut receiver) = oneshot::channel::<IsolateHandle>();
let has_client = authed_client.is_some();
let authed_client = authed_client.cloned();
return timeout(
std::time::Duration::from_millis(10000),
tokio::task::spawn_blocking(move || {
let mut ops = vec![op_get_context()];
if by_id.is_some() && authed_client.is_some() {
ops.push(op_get_result());
ops.push(op_get_id());
}
let ext = Extension { name: "js_eval", ops: ops.into(), ..Default::default() };
let exts = vec![ext];
// Use our snapshot to provision our new runtime
let options = RuntimeOptions {
extensions: exts,
// startup_snapshot: Some(Snapshot::Static(buffer)),
..Default::default()
};
let mut context_keys = transform_context
.keys()
.filter(|x| expr.contains(&x.to_string()))
.map(|x| x.clone())
.collect_vec();
if !context_keys.contains(&"previous_result".to_string())
&& (p_ids.is_some() && p_ids.as_ref().unwrap().iter().any(|x| expr.contains(x)))
|| expr.contains("error")
{
// tracing::error!("PREVIOUS_RESULT");
context_keys.push("previous_result".to_string());
}
let has_flow_input = expr.contains("flow_input");
if has_flow_input {
context_keys.push("flow_input".to_string())
}
let mut js_runtime = JsRuntime::new(options);
{
let op_state = js_runtime.op_state();
let mut op_state = op_state.borrow_mut();
let mut client = authed_client.clone();
if let Some(client) = client.as_mut() {
client.force_client = Some(
reqwest::ClientBuilder::new()
.user_agent("windmill/beta")
.danger_accept_invalid_certs(
std::env::var("ACCEPT_INVALID_CERTS").is_ok(),
)
.build()
.unwrap(),
);
if authed_client.is_some() {
ops.extend([
// An op for summing an array of numbers
// The op-layer automatically deserializes inputs
// and serializes the returned Result & value
op_variable(),
op_resource(),
])
}
op_state.put(OptAuthedClient(client));
op_state.put(TransformContext {
flow_input: if has_flow_input { flow_input } else { None },
envs: transform_context
.into_iter()
.filter(|(a, _)| context_keys.contains(a))
.collect(),
})
}
sender
.send(js_runtime.v8_isolate().thread_safe_handle())
.map_err(|_| Error::ExecutionErr("impossible to send v8 isolate".to_string()))?;
if by_id.is_some() && authed_client.is_some() {
ops.push(op_get_result());
ops.push(op_get_id());
}
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;
let ext = Extension { name: "js_eval", ops: ops.into(), ..Default::default() };
let exts = vec![ext];
// Use our snapshot to provision our new runtime
let options = RuntimeOptions {
extensions: exts,
// startup_snapshot: Some(Snapshot::Static(buffer)),
..Default::default()
};
// pretty frail but this it to make the expr more user friendly and not require the user to write await
let expr = ["variable", "resource"]
.into_iter()
.fold(expr, replace_with_await);
let mut context_keys = transform_context
.keys()
.filter(|x| expr.contains(&x.to_string()))
.map(|x| x.clone())
.collect_vec();
let expr = replace_with_await_result(expr);
if !context_keys.contains(&"previous_result".to_string())
&& (p_ids.is_some() && p_ids.as_ref().unwrap().iter().any(|x| expr.contains(x)))
|| expr.contains("error")
{
// tracing::error!("PREVIOUS_RESULT");
context_keys.push("previous_result".to_string());
}
let has_flow_input = expr.contains("flow_input");
if has_flow_input {
context_keys.push("flow_input".to_string())
}
let r = runtime.block_on(eval(
&mut js_runtime,
&expr,
context_keys,
by_id,
has_client,
ctx,
))?;
let mut js_runtime = JsRuntime::new(options);
{
let op_state = js_runtime.op_state();
let mut op_state = op_state.borrow_mut();
let mut client = authed_client.clone();
if let Some(client) = client.as_mut() {
client.force_client = Some(
reqwest::ClientBuilder::new()
.user_agent("windmill/beta")
.danger_accept_invalid_certs(
std::env::var("ACCEPT_INVALID_CERTS").is_ok(),
)
.build()
.unwrap(),
);
}
op_state.put(OptAuthedClient(client));
op_state.put(TransformContext {
flow_input: if has_flow_input { flow_input } else { None },
envs: transform_context
.into_iter()
.filter(|(a, _)| context_keys.contains(a))
.collect(),
})
}
Ok(r) as anyhow::Result<Box<RawValue>>
}),
)
.await
.map_err(|_| {
if let Ok(isolate) = receiver.try_recv() {
isolate.terminate_execution();
};
Error::ExecutionErr(format!(
"The expression of evaluation `{expr2}` took too long to execute (>10000ms)"
))
})??
sender
.send(js_runtime.v8_isolate().thread_safe_handle())
.map_err(|_| {
Error::ExecutionErr("impossible to send v8 isolate".to_string())
})?;
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;
// pretty frail but this it to make the expr more user friendly and not require the user to write await
let expr = ["variable", "resource"]
.into_iter()
.fold(expr, replace_with_await);
let expr = replace_with_await_result(expr);
let r = runtime.block_on(eval(
&mut js_runtime,
&expr,
context_keys,
by_id,
has_client,
ctx,
))?;
Ok(r) as anyhow::Result<Box<RawValue>>
}),
)
.await
.map_err(|_| {
if let Ok(isolate) = receiver.try_recv() {
isolate.terminate_execution();
};
Error::ExecutionErr(format!(
"The expression of evaluation `{expr2}` took too long to execute (>10000ms)"
))
})??;
}
}
#[cfg(feature = "deno_core")]
fn replace_with_await(expr: String, fn_name: &str) -> String {
let sep = format!("{}(", fn_name);
let mut split = expr.split(&sep);
@@ -345,10 +379,12 @@ lazy_static! {
Regex::new(r"^(https?)://(([^:@\s]+):([^:@\s]+)@)?([^:@\s]+)(:(\d+))?$").unwrap();
}
#[cfg(feature = "deno_core")]
fn replace_with_await_result(expr: String) -> String {
RE.replace_all(&expr, "(await $r)").to_string()
}
#[cfg(feature = "deno_core")]
fn add_closing_bracket(s: &str) -> String {
let mut s = s.to_string();
let mut level = 1;
@@ -368,6 +404,7 @@ fn add_closing_bracket(s: &str) -> String {
s
}
#[cfg(feature = "deno_core")]
async fn eval(
context: &mut JsRuntime,
expr: &str,
@@ -508,6 +545,7 @@ function get_from_env(name) {{
// }
// TODO: Can we a) share the api configuration here somehow or b) just implement this natively in deno, via the deno client?
#[cfg(feature = "deno_core")]
#[op2(async)]
#[string]
async fn op_variable(
@@ -522,6 +560,7 @@ async fn op_variable(
}
}
#[cfg(feature = "deno_core")]
#[op2(async)]
#[string]
async fn op_get_result(
@@ -540,6 +579,7 @@ async fn op_get_result(
}
}
#[cfg(feature = "deno_core")]
#[op2(async)]
#[string]
async fn op_get_id(
@@ -563,6 +603,7 @@ async fn op_get_id(
}
}
#[cfg(feature = "deno_core")]
#[op2(async)]
#[string]
async fn op_resource(
@@ -580,11 +621,13 @@ async fn op_resource(
}
}
#[cfg(feature = "deno_core")]
pub struct TransformContext {
pub envs: HashMap<String, Arc<Box<RawValue>>>,
pub flow_input: Option<mappable_rc::Marc<HashMap<String, Box<RawValue>>>>,
}
#[cfg(feature = "deno_core")]
#[op2]
#[string]
fn op_get_context(op_state: Rc<RefCell<OpState>>, #[string] id: &str) -> String {
@@ -605,6 +648,7 @@ fn op_get_context(op_state: Rc<RefCell<OpState>>, #[string] id: &str) -> String
}
}
#[cfg(feature = "deno_core")]
pub fn transpile_ts(expr: String) -> anyhow::Result<String> {
let parsed = deno_ast::parse_module(ParseParams {
specifier: url::Url::parse("file:///eval.ts")?,
@@ -621,21 +665,30 @@ pub fn transpile_ts(expr: String) -> anyhow::Result<String> {
.text)
}
#[cfg(not(feature = "deno_core"))]
pub fn transpile_ts(_expr: String) -> anyhow::Result<String> {
Ok("require deno".to_string())
}
#[cfg(feature = "deno_core")]
static RUNTIME_SNAPSHOT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/FETCH_SNAPSHOT.bin"));
#[cfg(feature = "deno_core")]
pub struct MainArgs {
args: Vec<Option<Box<RawValue>>>,
}
#[cfg(feature = "deno_core")]
pub struct LogString {
pub s: String,
}
#[cfg(feature = "deno_core")]
pub struct NativeAnnotation {
pub useragent: Option<String>,
pub proxy: Option<(String, Option<(String, String)>)>,
}
#[cfg(feature = "deno_core")]
pub fn get_annotation(inner_content: &str) -> NativeAnnotation {
let mut res = NativeAnnotation { useragent: None, proxy: None };
@@ -655,6 +708,7 @@ pub fn get_annotation(inner_content: &str) -> NativeAnnotation {
res
}
#[cfg(feature = "deno_core")]
fn capture_proxy(s: &str) -> Option<(String, Option<(String, String)>)> {
RE_PROXY.captures(s).map(|x| {
(
@@ -675,7 +729,27 @@ fn capture_proxy(s: &str) -> Option<(String, Option<(String, String)>)> {
)
})
}
#[cfg(not(feature = "deno_core"))]
pub async fn eval_fetch_timeout(
_env_code: String,
_ts_expr: String,
_js_expr: String,
_args: Option<&Json<HashMap<String, Box<RawValue>>>>,
_job_id: Uuid,
_job_timeout: Option<i32>,
_db: &DB,
_mem_peak: &mut i32,
_canceled_by: &mut Option<CanceledBy>,
_worker_name: &str,
_w_id: &str,
_load_client: bool,
_occupation_metrics: &mut OccupancyMetrics,
) -> anyhow::Result<(Box<RawValue>, String)> {
use serde_json::value::to_raw_value;
Ok((to_raw_value("require deno_core").unwrap(), "".to_string()))
}
#[cfg(feature = "deno_core")]
pub async fn eval_fetch_timeout(
env_code: String,
ts_expr: String,
@@ -851,8 +925,10 @@ pub async fn eval_fetch_timeout(
Ok((res, format!("{extra_logs}{logs}")))
}
#[cfg(feature = "deno_core")]
const WINDMILL_CLIENT: &str = include_str!("./windmill-client.js");
#[cfg(feature = "deno_core")]
async fn eval_fetch(
js_runtime: &mut JsRuntime,
expr: &str,
@@ -898,6 +974,7 @@ import("file:///eval.ts").then((module) => module.main(...args)).then(JSON.strin
Ok(unsafe_raw(r.unwrap_or_else(|| "null".to_string())))
}
#[cfg(feature = "deno_core")]
#[op2]
#[serde]
fn op_get_static_args(op_state: Rc<RefCell<OpState>>) -> Vec<Option<String>> {
@@ -910,6 +987,7 @@ fn op_get_static_args(op_state: Rc<RefCell<OpState>>) -> Vec<Option<String>> {
.collect_vec()
}
#[cfg(feature = "deno_core")]
#[op2(fast)]
fn op_log(op_state: Rc<RefCell<OpState>>, #[string] log: &str) {
// tracing::error!("log: |{}|", log);
@@ -920,6 +998,7 @@ fn op_log(op_state: Rc<RefCell<OpState>>, #[string] log: &str) {
.push_str(log);
}
#[cfg(feature = "deno_core")]
#[cfg(test)]
mod tests {
+24 -15
View File
@@ -183,10 +183,13 @@ export type InstanceSyncOptions = {
yes?: boolean;
};
export async function pickInstance(opts: InstanceSyncOptions, allowNew: boolean) {
export async function pickInstance(
opts: InstanceSyncOptions,
allowNew: boolean
) {
const instances = await allInstances();
if (opts.baseUrl && opts.token) {
log.info("Using instance fully defined by --base-url and --token")
log.info("Using instance fully defined by --base-url and --token");
return {
name: "custom",
remote: opts.baseUrl,
@@ -335,12 +338,14 @@ async function instancePull(opts: GlobalOptions & InstanceSyncOptions) {
);
if (localWorkspacesToDelete.length > 0) {
const confirmDelete = await Confirm.prompt({
message:
"Do you want to delete the local copy of workspaces that don't exist anymore on the instance?\n" +
localWorkspacesToDelete.map((w) => w.workspaceId).join(", "),
default: true,
});
const confirmDelete =
opts.yes ||
(await Confirm.prompt({
message:
"Do you want to delete the local copy of workspaces that don't exist anymore on the instance?\n" +
localWorkspacesToDelete.map((w) => w.workspaceId).join(", "),
default: true,
}));
if (confirmDelete) {
for (const workspace of localWorkspacesToDelete) {
@@ -485,12 +490,14 @@ async function instancePush(opts: GlobalOptions & InstanceSyncOptions) {
(w) => !localWorkspaces.find((l) => l.workspaceId === w.id)
);
if (workspacesToDelete.length > 0) {
const confirmDelete = await Confirm.prompt({
message:
"Do you want to delete the following remote workspaces that don't exist locally?\n" +
workspacesToDelete.map((w) => w.id).join(", "),
default: true,
});
const confirmDelete =
opts.yes ||
(await Confirm.prompt({
message:
"Do you want to delete the following remote workspaces that don't exist locally?\n" +
workspacesToDelete.map((w) => w.id).join(", "),
default: true,
}));
if (confirmDelete) {
for (const workspace of workspacesToDelete) {
@@ -544,7 +551,9 @@ async function whoami(opts: {}) {
log.info(colors.green.underline(`global whoami infos:`));
log.info(JSON.stringify(whoamiInfo, null, 2));
} catch (error) {
log.error(colors.red(`Failed to retrieve whoami information: ${error.message}`));
log.error(
colors.red(`Failed to retrieve whoami information: ${error.message}`)
);
}
}