From aebde3a382fb7326ac77955efba8ff05476b75d0 Mon Sep 17 00:00:00 2001 From: Willem Date: Fri, 29 Aug 2025 13:12:39 -0400 Subject: [PATCH 1/4] feat(example): added an example of packaging a rust app from github --- .gitmodules | 3 + .../src/main.rs | 1 + examples/rust/src/main.rs | 3 +- examples/try_rust_webapp/Cargo.toml | 12 + examples/try_rust_webapp/src/main.rs | 53 +++++ examples/try_rust_webapp/tryrust.org | 1 + harmony/src/modules/application/rust.rs | 221 +++++++++++------- 7 files changed, 203 insertions(+), 91 deletions(-) create mode 100644 .gitmodules create mode 100644 examples/try_rust_webapp/Cargo.toml create mode 100644 examples/try_rust_webapp/src/main.rs create mode 160000 examples/try_rust_webapp/tryrust.org diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4438aa2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "examples/try_rust_webapp/tryrust.org"] + path = examples/try_rust_webapp/tryrust.org + url = https://github.com/rust-dd/tryrust.org.git diff --git a/examples/application_monitoring_with_tenant/src/main.rs b/examples/application_monitoring_with_tenant/src/main.rs index 9ab0bf6..4691bf9 100644 --- a/examples/application_monitoring_with_tenant/src/main.rs +++ b/examples/application_monitoring_with_tenant/src/main.rs @@ -29,6 +29,7 @@ async fn main() { domain: Url::Url(url::Url::parse("https://rustapp.harmony.example.com").unwrap()), project_root: PathBuf::from("./examples/rust/webapp"), framework: Some(RustWebFramework::Leptos), + service_port: 3000, }); let webhook_receiver = WebhookReceiver { diff --git a/examples/rust/src/main.rs b/examples/rust/src/main.rs index feb92ef..d3d52ec 100644 --- a/examples/rust/src/main.rs +++ b/examples/rust/src/main.rs @@ -19,8 +19,9 @@ async fn main() { let application = Arc::new(RustWebapp { name: "harmony-example-rust-webapp".to_string(), domain: Url::Url(url::Url::parse("https://rustapp.harmony.example.com").unwrap()), - project_root: PathBuf::from("./webapp"), // Relative from 'harmony-path' param + project_root: PathBuf::from("./webapp"), framework: Some(RustWebFramework::Leptos), + service_port: 3000, }); let discord_receiver = DiscordWebhook { diff --git a/examples/try_rust_webapp/Cargo.toml b/examples/try_rust_webapp/Cargo.toml new file mode 100644 index 0000000..883de79 --- /dev/null +++ b/examples/try_rust_webapp/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "example-try-rust-webapp" +edition = "2024" +version.workspace = true +readme.workspace = true +license.workspace = true + +[dependencies] +harmony = { version = "0.1.0", path = "../../harmony" } +harmony_cli = { version = "0.1.0", path = "../../harmony_cli" } +tokio.workspace = true +url.workspace = true diff --git a/examples/try_rust_webapp/src/main.rs b/examples/try_rust_webapp/src/main.rs new file mode 100644 index 0000000..22175e3 --- /dev/null +++ b/examples/try_rust_webapp/src/main.rs @@ -0,0 +1,53 @@ +use std::{path::PathBuf, sync::Arc}; + +use harmony::{ + inventory::Inventory, + modules::{ + application::{ + ApplicationScore, RustWebFramework, RustWebapp, + features::{ContinuousDelivery, Monitoring}, + }, + monitoring::alert_channel::{ + discord_alert_channel::DiscordWebhook, webhook_receiver::WebhookReceiver, + }, + }, + topology::{K8sAnywhereTopology, Url}, +}; + +#[tokio::main] +async fn main() { + let application = Arc::new(RustWebapp { + name: "harmony-example-tryrust".to_string(), + domain: Url::Url(url::Url::parse("https://tryrust.harmony.example.com").unwrap()), + project_root: PathBuf::from("./tryrust.org"), + framework: Some(RustWebFramework::Leptos), + service_port: 8080, + }); + + let discord_receiver = DiscordWebhook { + name: "test-discord".to_string(), + url: Url::Url(url::Url::parse("https://discord.doesnt.exist.com").unwrap()), + }; + + let app = ApplicationScore { + features: vec![ + Box::new(ContinuousDelivery { + application: application.clone(), + }), + Box::new(Monitoring { + application: application.clone(), + alert_receiver: vec![Box::new(discord_receiver)], + }), + ], + application, + }; + + harmony_cli::run( + Inventory::autoload(), + K8sAnywhereTopology::from_env(), + vec![Box::new(app)], + None, + ) + .await + .unwrap(); +} diff --git a/examples/try_rust_webapp/tryrust.org b/examples/try_rust_webapp/tryrust.org new file mode 160000 index 0000000..0f9ba14 --- /dev/null +++ b/examples/try_rust_webapp/tryrust.org @@ -0,0 +1 @@ +Subproject commit 0f9ba145172867f467e5320b37d07a5bbb7dd438 diff --git a/harmony/src/modules/application/rust.rs b/harmony/src/modules/application/rust.rs index da1e594..9a4eddf 100644 --- a/harmony/src/modules/application/rust.rs +++ b/harmony/src/modules/application/rust.rs @@ -61,6 +61,7 @@ pub struct RustWebapp { pub domain: Url, /// The path to the root of the Rust project to be containerized. pub project_root: PathBuf, + pub service_port: u32, pub framework: Option, } @@ -160,45 +161,56 @@ impl RustWebapp { image_name: &str, ) -> Result> { debug!("Generating Dockerfile for '{}'", self.name); - let _dockerfile_path = self.build_dockerfile()?; - + let dockerfile = self.get_or_build_dockerfile(); let docker = Docker::connect_with_socket_defaults().unwrap(); - let quiet = !log_enabled!(log::Level::Debug); - - let build_image_options = bollard::query_parameters::BuildImageOptionsBuilder::default() - .dockerfile("Dockerfile.harmony") - .t(image_name) - .q(quiet) - .version(bollard::query_parameters::BuilderVersion::BuilderV1) - .platform("linux/x86_64"); - - let mut temp_tar_builder = tar::Builder::new(Vec::new()); - temp_tar_builder - .append_dir_all("", self.project_root.clone()) - .unwrap(); - let archive = temp_tar_builder - .into_inner() - .expect("couldn't finish creating tar"); - let archived_files = Archive::new(archive.as_slice()) - .entries() + match dockerfile .unwrap() - .map(|entry| entry.unwrap().path().unwrap().into_owned()) - .collect::>(); + .file_name() + .and_then(|os_str| os_str.to_str()) + { + Some(path_str) => { + debug!("Building from dockerfile {}", path_str); + let build_image_options = + bollard::query_parameters::BuildImageOptionsBuilder::default() + .dockerfile(path_str) + .t(image_name) + .q(quiet) + .version(bollard::query_parameters::BuilderVersion::BuilderV1) + .platform("linux/x86_64"); + let mut temp_tar_builder = tar::Builder::new(Vec::new()); + temp_tar_builder + .append_dir_all("", self.project_root.clone()) + .unwrap(); + let archive = temp_tar_builder + .into_inner() + .expect("couldn't finish creating tar"); + let archived_files = Archive::new(archive.as_slice()) + .entries() + .unwrap() + .map(|entry| entry.unwrap().path().unwrap().into_owned()) + .collect::>(); - debug!("files in docker tar: {:#?}", archived_files); + debug!("files in docker tar: {:#?}", archived_files); - let mut image_build_stream = docker.build_image( - build_image_options.build(), - None, - Some(body_full(archive.into())), - ); + let mut image_build_stream = docker.build_image( + build_image_options.build(), + None, + Some(body_full(archive.into())), + ); - while let Some(msg) = image_build_stream.next().await { - debug!("Message: {msg:?}"); + while let Some(msg) = image_build_stream.next().await { + debug!("Message: {msg:?}"); + } + + Ok(image_name.to_string()) + } + + None => Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Path is not valid UTF-8", + ))), } - - Ok(image_name.to_string()) } /// Tags and pushes a Docker image to the configured remote registry. @@ -274,8 +286,11 @@ impl RustWebapp { "groupadd -r appgroup && useradd -r -s /bin/false -g appgroup appuser", )); - dockerfile.push(ENV::from("LEPTOS_SITE_ADDR=0.0.0.0:3000")); - dockerfile.push(EXPOSE::from("3000/tcp")); + dockerfile.push(ENV::from(format!( + "LEPTOS_SITE_ADDR=0.0.0.0:{}", + self.service_port + ))); + dockerfile.push(EXPOSE::from(format!("{}/tcp", self.service_port))); dockerfile.push(WORKDIR::from("/home/appuser")); // Copy static files @@ -396,7 +411,7 @@ image: service: type: ClusterIP - port: 3000 + port: {} ingress: enabled: true @@ -416,112 +431,123 @@ ingress: - chart-example.local "#, - chart_name, image_repo, image_tag, self.name + chart_name, image_repo, image_tag, self.service_port, self.name ); fs::write(chart_dir.join("values.yaml"), values_yaml)?; // Create templates/_helpers.tpl - let helpers_tpl = r#" -{{/* + let helpers_tpl = format!( + r#" +{{{{/* Expand the name of the chart. -*/}} -{{- define "chart.name" -}} -{{- default .Chart.Name $.Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} +*/}}}} +{{{{- define "chart.name" -}}}} +{{{{- default .Chart.Name $.Values.nameOverride | trunc 63 | trimSuffix "-" }}}} +{{{{- end }}}} -{{/* +{{{{/* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -*/}} -{{- define "chart.fullname" -}} -{{- $name := default .Chart.Name $.Values.nameOverride }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -"#; +*/}}}} +{{{{- define "chart.fullname" -}}}} +{{{{- $name := default .Chart.Name $.Values.nameOverride }}}} +{{{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}}} +{{{{- end }}}} +"# + ); fs::write(templates_dir.join("_helpers.tpl"), helpers_tpl)?; // Create templates/service.yaml - let service_yaml = r#" + let service_yaml = format!( + r#" apiVersion: v1 kind: Service metadata: - name: {{ include "chart.fullname" . }} + name: {{{{ include "chart.fullname" . }}}} spec: - type: {{ $.Values.service.type }} + type: {{{{ $.Values.service.type }}}} ports: - name: main - port: {{ $.Values.service.port | default 3000 }} - targetPort: {{ $.Values.service.port | default 3000 }} + port: {{{{ $.Values.service.port | default {} }}}} + targetPort: {{{{ $.Values.service.port | default {} }}}} protocol: TCP selector: - app: {{ include "chart.name" . }} -"#; + app: {{{{ include "chart.name" . }}}} +"#, + self.service_port, self.service_port + ); fs::write(templates_dir.join("service.yaml"), service_yaml)?; // Create templates/deployment.yaml - let deployment_yaml = r#" + let deployment_yaml = format!( + r#" apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "chart.fullname" . }} + name: {{{{ include "chart.fullname" . }}}} spec: - replicas: {{ $.Values.replicaCount }} + replicas: {{{{ $.Values.replicaCount }}}} selector: matchLabels: - app: {{ include "chart.name" . }} + app: {{{{ include "chart.name" . }}}} template: metadata: labels: - app: {{ include "chart.name" . }} + app: {{{{ include "chart.name" . }}}} spec: containers: - - name: {{ .Chart.Name }} - image: "{{ $.Values.image.repository }}:{{ $.Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ $.Values.image.pullPolicy }} + - name: {{{{ .Chart.Name }}}} + image: "{{{{ $.Values.image.repository }}}}:{{{{ $.Values.image.tag | default .Chart.AppVersion }}}}" + imagePullPolicy: {{{{ $.Values.image.pullPolicy }}}} ports: - name: main - containerPort: {{ $.Values.service.port | default 3000 }} + containerPort: {{{{ $.Values.service.port | default {} }}}} protocol: TCP -"#; +"#, + self.service_port + ); fs::write(templates_dir.join("deployment.yaml"), deployment_yaml)?; // Create templates/ingress.yaml - let ingress_yaml = r#" -{{- if $.Values.ingress.enabled -}} + let ingress_yaml = format!( + r#" +{{{{- if $.Values.ingress.enabled -}}}} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: {{ include "chart.fullname" . }} + name: {{{{ include "chart.fullname" . }}}} annotations: - {{- toYaml $.Values.ingress.annotations | nindent 4 }} + {{{{- toYaml $.Values.ingress.annotations | nindent 4 }}}} spec: - {{- if $.Values.ingress.tls }} + {{{{- if $.Values.ingress.tls }}}} tls: - {{- range $.Values.ingress.tls }} + {{{{- range $.Values.ingress.tls }}}} - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} + {{{{- range .hosts }}}} + - {{{{ . | quote }}}} + {{{{- end }}}} + secretName: {{{{ .secretName }}}} + {{{{- end }}}} + {{{{- end }}}} rules: - {{- range $.Values.ingress.hosts }} - - host: {{ .host | quote }} + {{{{- range $.Values.ingress.hosts }}}} + - host: {{{{ .host | quote }}}} http: paths: - {{- range .paths }} - - path: {{ .path }} - pathType: {{ .pathType }} + {{{{- range .paths }}}} + - path: {{{{ .path }}}} + pathType: {{{{ .pathType }}}} backend: service: - name: {{ include "chart.fullname" $ }} + name: {{{{ include "chart.fullname" $ }}}} port: - number: {{ $.Values.service.port | default 3000 }} - {{- end }} - {{- end }} -{{- end }} -"#; + number: {{{{ $.Values.service.port | default {} }}}} + {{{{- end }}}} + {{{{- end }}}} +{{{{- end }}}} +"#, + self.service_port + ); fs::write(templates_dir.join("ingress.yaml"), ingress_yaml)?; Ok(chart_dir) @@ -573,7 +599,6 @@ spec: let chart_file_name = packaged_chart_path.file_stem().unwrap().to_str().unwrap(); let oci_push_url = format!("oci://{}/{}", *REGISTRY_URL, *REGISTRY_PROJECT); let oci_pull_url = format!("{oci_push_url}/{}-chart", self.name); - debug!( "Pushing Helm chart {} to {}", packaged_chart_path.to_string_lossy(), @@ -592,4 +617,20 @@ spec: debug!("push url {oci_push_url}"); Ok(format!("{}:{}", oci_pull_url, version)) } + + fn get_or_build_dockerfile(&self) -> Result> { + let existing_dockerfile = self.project_root.join("Dockerfile"); + + debug!("project_root = {:?}", self.project_root); + + debug!("checking = {:?}", existing_dockerfile); + if existing_dockerfile.exists() { + debug!( + "Checking path {:#?} for existing Dockerfile", + self.project_root.clone() + ); + return Ok(existing_dockerfile); + } + self.build_dockerfile() + } } -- 2.39.5 From 32ffc3ef61db74d8c7528cf824d410df9a6968f7 Mon Sep 17 00:00:00 2001 From: Willem Date: Wed, 3 Sep 2025 16:13:51 -0400 Subject: [PATCH 2/4] fix: cache works, modified tar building function to ignore adding autogenerated files from harmony, .git, .github, target, and node_modules file paths\ > tar building function orders files and sets mtime to zero so it is consistent across docker image builds --- Cargo.lock | 48 +++++++++++++++ harmony/Cargo.toml | 1 + harmony/src/modules/application/rust.rs | 81 +++++++++++++++++++------ 3 files changed, 112 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1295be..ad3f964 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1685,6 +1685,16 @@ dependencies = [ "url", ] +[[package]] +name = "example-try-rust-webapp" +version = "0.1.0" +dependencies = [ + "harmony", + "harmony_cli", + "tokio", + "url", +] + [[package]] name = "example-tui" version = "0.1.0" @@ -1700,6 +1710,15 @@ dependencies = [ "url", ] +[[package]] +name = "example_validate_ceph_cluster_health" +version = "0.1.0" +dependencies = [ + "harmony", + "harmony_cli", + "tokio", +] + [[package]] name = "eyre" version = "0.6.12" @@ -2097,6 +2116,7 @@ dependencies = [ "tokio-util", "url", "uuid", + "walkdir", ] [[package]] @@ -4671,6 +4691,15 @@ dependencies = [ "cipher", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.27" @@ -5961,6 +5990,16 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -6115,6 +6154,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" +dependencies = [ + "windows-sys 0.60.2", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/harmony/Cargo.toml b/harmony/Cargo.toml index 1ba4c94..47e6c5c 100644 --- a/harmony/Cargo.toml +++ b/harmony/Cargo.toml @@ -68,6 +68,7 @@ tar.workspace = true base64.workspace = true once_cell = "1.21.3" harmony-secret-derive = { version = "0.1.0", path = "../harmony_secret_derive" } +walkdir = "2.5.0" [dev-dependencies] pretty_assertions.workspace = true diff --git a/harmony/src/modules/application/rust.rs b/harmony/src/modules/application/rust.rs index 9a4eddf..84605f8 100644 --- a/harmony/src/modules/application/rust.rs +++ b/harmony/src/modules/application/rust.rs @@ -1,4 +1,5 @@ -use std::fs; +use std::fs::{self, File}; +use std::io::Read; use std::path::{Path, PathBuf}; use std::process; use std::sync::Arc; @@ -12,7 +13,8 @@ use dockerfile_builder::instruction_builder::CopyBuilder; use futures_util::StreamExt; use log::{debug, info, log_enabled}; use serde::Serialize; -use tar::Archive; +use tar::{Archive, Builder, Header}; +use walkdir::WalkDir; use crate::config::{REGISTRY_PROJECT, REGISTRY_URL}; use crate::{ @@ -162,7 +164,6 @@ impl RustWebapp { ) -> Result> { debug!("Generating Dockerfile for '{}'", self.name); let dockerfile = self.get_or_build_dockerfile(); - let docker = Docker::connect_with_socket_defaults().unwrap(); let quiet = !log_enabled!(log::Level::Debug); match dockerfile .unwrap() @@ -171,6 +172,14 @@ impl RustWebapp { { Some(path_str) => { debug!("Building from dockerfile {}", path_str); + + let tar_data = self + .create_deterministic_tar(&self.project_root.clone()) + .await + .unwrap(); + + let docker = Docker::connect_with_socket_defaults().unwrap(); + let build_image_options = bollard::query_parameters::BuildImageOptionsBuilder::default() .dockerfile(path_str) @@ -178,25 +187,11 @@ impl RustWebapp { .q(quiet) .version(bollard::query_parameters::BuilderVersion::BuilderV1) .platform("linux/x86_64"); - let mut temp_tar_builder = tar::Builder::new(Vec::new()); - temp_tar_builder - .append_dir_all("", self.project_root.clone()) - .unwrap(); - let archive = temp_tar_builder - .into_inner() - .expect("couldn't finish creating tar"); - let archived_files = Archive::new(archive.as_slice()) - .entries() - .unwrap() - .map(|entry| entry.unwrap().path().unwrap().into_owned()) - .collect::>(); - - debug!("files in docker tar: {:#?}", archived_files); let mut image_build_stream = docker.build_image( build_image_options.build(), None, - Some(body_full(archive.into())), + Some(body_full(tar_data.into())), ); while let Some(msg) = image_build_stream.next().await { @@ -213,6 +208,56 @@ impl RustWebapp { } } + ///normalizes timestamp and ignores files that will bust the docker cach + async fn create_deterministic_tar( + &self, + project_root: &std::path::Path, + ) -> Result, Box> { + debug!("building tar file from project root {:#?}", project_root); + let mut tar_data = Vec::new(); + { + let mut builder = Builder::new(&mut tar_data); + let ignore_prefixes = [ + "target", + ".git", + ".github", + ".harmony_generated", + "node_modules", + ]; + let mut entries: Vec<_> = WalkDir::new(project_root) + .into_iter() + .filter_map(Result::ok) + .filter(|e| e.file_type().is_file()) + .filter(|e| { + let rel_path = e.path().strip_prefix(project_root).unwrap(); + !ignore_prefixes + .iter() + .any(|prefix| rel_path.starts_with(prefix)) + }) + .collect(); + entries.sort_by_key(|e| e.path().to_owned()); + + for entry in entries { + let path = entry.path(); + let rel_path = path.strip_prefix(project_root).unwrap(); + + let mut file = fs::File::open(path)?; + let mut header = Header::new_gnu(); + + header.set_size(entry.metadata()?.len()); + header.set_mode(0o644); + header.set_mtime(0); + header.set_uid(0); + header.set_gid(0); + + builder.append_data(&mut header, rel_path, &mut file)?; + } + + builder.finish()?; + } + Ok(tar_data) + } + /// Tags and pushes a Docker image to the configured remote registry. async fn push_docker_image( &self, -- 2.39.5 From 8cfceea0dd7216fb8fa4fc465ca4659c71da78b1 Mon Sep 17 00:00:00 2001 From: Willem Date: Fri, 5 Sep 2025 11:19:03 -0400 Subject: [PATCH 3/4] fix: corrected name of harmony_secret_derive crate --- harmony/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/harmony/Cargo.toml b/harmony/Cargo.toml index 0fe3fa8..9372a7b 100644 --- a/harmony/Cargo.toml +++ b/harmony/Cargo.toml @@ -66,7 +66,6 @@ tar.workspace = true base64.workspace = true thiserror.workspace = true once_cell = "1.21.3" -harmony-secret-derive = { version = "0.1.0", path = "../harmony_secret_derive" } walkdir = "2.5.0" harmony_inventory_agent = { path = "../harmony_inventory_agent" } harmony_secret_derive = { version = "0.1.0", path = "../harmony_secret_derive" } -- 2.39.5 From d8866cc1a628526aa050610bbd166d4d61866d63 Mon Sep 17 00:00:00 2001 From: Willem Date: Fri, 5 Sep 2025 11:22:21 -0400 Subject: [PATCH 4/4] fix: build fail --- Cargo.lock | 5 +++++ examples/try_rust_webapp/Cargo.toml | 13 +++++++++---- examples/try_rust_webapp/src/main.rs | 7 +++---- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66d3cd8..8cdfe97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1842,8 +1842,13 @@ dependencies = [ name = "example-try-rust-webapp" version = "0.1.0" dependencies = [ + "base64 0.22.1", + "env_logger", "harmony", "harmony_cli", + "harmony_macros", + "harmony_types", + "log", "tokio", "url", ] diff --git a/examples/try_rust_webapp/Cargo.toml b/examples/try_rust_webapp/Cargo.toml index 883de79..fc4f8a1 100644 --- a/examples/try_rust_webapp/Cargo.toml +++ b/examples/try_rust_webapp/Cargo.toml @@ -6,7 +6,12 @@ readme.workspace = true license.workspace = true [dependencies] -harmony = { version = "0.1.0", path = "../../harmony" } -harmony_cli = { version = "0.1.0", path = "../../harmony_cli" } -tokio.workspace = true -url.workspace = true +harmony = { path = "../../harmony" } +harmony_cli = { path = "../../harmony_cli" } +harmony_types = { path = "../../harmony_types" } +harmony_macros = { path = "../../harmony_macros" } +tokio = { workspace = true } +log = { workspace = true } +env_logger = { workspace = true } +url = { workspace = true } +base64.workspace = true diff --git a/examples/try_rust_webapp/src/main.rs b/examples/try_rust_webapp/src/main.rs index 22175e3..6e1ab63 100644 --- a/examples/try_rust_webapp/src/main.rs +++ b/examples/try_rust_webapp/src/main.rs @@ -7,12 +7,11 @@ use harmony::{ ApplicationScore, RustWebFramework, RustWebapp, features::{ContinuousDelivery, Monitoring}, }, - monitoring::alert_channel::{ - discord_alert_channel::DiscordWebhook, webhook_receiver::WebhookReceiver, - }, + monitoring::alert_channel::discord_alert_channel::DiscordWebhook, }, - topology::{K8sAnywhereTopology, Url}, + topology::K8sAnywhereTopology, }; +use harmony_types::net::Url; #[tokio::main] async fn main() { -- 2.39.5