Skip to content

Commit

Permalink
docs: Finalize blog post. Add tests. (#1436)
Browse files Browse the repository at this point in the history
* Update deps.

* Update layout.

* Finalize blog post.

* Fix tests.

* Use millis.
  • Loading branch information
milesj committed Apr 18, 2024
1 parent f8a1a31 commit 26580f0
Show file tree
Hide file tree
Showing 16 changed files with 257 additions and 112 deletions.
144 changes: 72 additions & 72 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ exclude = ["tests/fixtures", "wasm/test-plugin"]
default-members = ["crates/cli"]

[workspace.dependencies]
async-trait = "0.1.79"
async-trait = "0.1.80"
cached = "0.49.3"
chrono = { version = "0.4.37", features = ["serde"] }
chrono = { version = "0.4.38", features = ["serde"] }
cd_env = "0.2.0"
ci_env = "0.3.0"
clap = { version = "4.5.4", default-features = false, features = [
Expand All @@ -35,7 +35,7 @@ dashmap = "5.5.3"
dirs = "5.0.1"
miette = "7.2.0"
once_cell = "1.19.0"
once_map = "0.4.16"
once_map = "0.4.17"
pathdiff = "0.2.1"
petgraph = { version = "0.6.4", default-features = false, features = [
"serde-1",
Expand All @@ -55,8 +55,8 @@ schematic = { version = "0.15.0", default-features = false, features = [
"schema",
] }
semver = "1.0.22"
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.115"
serde = { version = "1.0.198", features = ["derive"] }
serde_json = "1.0.116"
serde_yaml = "0.9.34"
starbase = "0.5.2"
starbase_archive = { version = "0.6.2", default-features = false, features = [
Expand Down
12 changes: 7 additions & 5 deletions crates/cli/src/commands/graph/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ use starbase_styles::color;

#[derive(Args, Clone, Debug)]
pub struct ActionGraphArgs {
#[arg(help = "Target to *only* graph")]
target: Option<TargetLocator>,
#[arg(help = "Targets to *only* graph")]
targets: Option<Vec<TargetLocator>>,

#[arg(long, help = "Include dependents of the focused target")]
#[arg(long, help = "Include dependents of the focused target(s)")]
dependents: bool,

#[arg(long, help = "Print the graph in DOT format")]
Expand All @@ -36,8 +36,10 @@ pub async fn internal_action_graph(
};

// Focus a target and its dependencies/dependents
if let Some(locator) = args.target.clone() {
action_graph_builder.run_task_by_target_locator(locator, &requirements)?;
if let Some(locators) = &args.targets {
for locator in locators {
action_graph_builder.run_task_by_target_locator(locator, &requirements)?;
}

// Show all targets and actions
} else {
Expand Down
27 changes: 25 additions & 2 deletions crates/cli/tests/run_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,29 @@ fn disambiguates_same_tasks_with_diff_args_envs() {
assert.success();
}

#[test]
fn runs_task_with_a_mutex_in_sequence() {
let sandbox = cases_sandbox();
let start = std::time::Instant::now();

let assert = sandbox.run_moon(|cmd| {
cmd.arg("run")
.arg("mutex:run1")
.arg("mutex:run2")
.arg("mutex:run3")
.arg("--log")
.arg("debug");
});

assert.success();

let stop = start.elapsed();

dbg!(&start, &stop);

assert!(stop.as_millis() > 3000);
}

#[cfg(not(windows))]
mod general {
use super::*;
Expand Down Expand Up @@ -697,11 +720,11 @@ mod hashing {
// Hashes change because `.moon/workspace.yml` is different from `walk_strategy`
assert_eq!(
hash_vcs,
"afa2e4bc5c48f5c649cae710560dfd2001e14fee5a8846d7ed30f00216fd8d03"
"4f65a90e3f44c850eda3e7dd64f801d743a8bef29a6fcc5231369e55cfa43ee9"
);
assert_eq!(
hash_glob,
"f552a883dc6d1d8c327fc7357c156948381c8da842e4b1a5f7fa933c8bd5988e"
"c0a7c576081e08902e8bdf4b191ca6621be4274f808081d78dc77619df058f4a"
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ source: crates/cli/tests/run_test.rs
expression: "[h1, h2, extract_hash_from_run(sandbox.path(), \"outputs:asDep\"),\n extract_hash_from_run(sandbox.path(), \"outputs:withDeps\")]"
---
[
"23f23836de62d0ba7434d9b3d03f7a6c1703e833baee5a5df5e8afb61148b6ea",
"ccd5f0239174d32dc6b51ce85e4acb30fe83478bfd33c585e174a989b1ed3526",
"a48aa57f0edf45fb589583de132843b8496b841c03c32188345dc940f5b28a76",
"300922a15e4ce8ed30ae280278a8a8339ad4b4821d86f2f9127cd392aa3e2a8f",
"5bbce721f289c545e29a23c475b4391510bca100f60829ec00566899632f3b68",
"9240d7fb59a8aa40f4eccd09295838d4f5d3bb5fe69a3b0c926cdb02f0280650",
"84bd6004ef5c203fc1041c6f95224eae9bd350f9254e425625d618d81a27fd94",
"63773b7f9e87ddc842f39000b18bed6387e4bb576fdc9d4b567efe412a0e8fca",
]
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ source: crates/cli/tests/run_test.rs
expression: "[extract_hash_from_run(sandbox.path(), \"outputs:asDep\"),\n extract_hash_from_run(sandbox.path(), \"outputs:withDeps\")]"
---
[
"23f23836de62d0ba7434d9b3d03f7a6c1703e833baee5a5df5e8afb61148b6ea",
"ccd5f0239174d32dc6b51ce85e4acb30fe83478bfd33c585e174a989b1ed3526",
"5bbce721f289c545e29a23c475b4391510bca100f60829ec00566899632f3b68",
"9240d7fb59a8aa40f4eccd09295838d4f5d3bb5fe69a3b0c926cdb02f0280650",
]
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ expression: assert.output()
Tasks: 2 completed
Time: 100ms

2d32b7bd13adc2bd754fb6479a9ef14af3b46a30872082dca0483058d28b0c2b
21f0e3dd03e9600145bbdbe635cefc1c685f1b20e5be118c9f19838cdb9c3782
21 changes: 20 additions & 1 deletion crates/core/action-pipeline/src/actions/run_task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use moon_workspace::Workspace;
use starbase_styles::color;
use std::env;
use std::sync::Arc;
use tokio::sync::RwLock;
use tokio::sync::{Mutex, RwLock};

const LOG_TARGET: &str = "moon:action:run-task";

Expand Down Expand Up @@ -108,9 +108,28 @@ pub async fn run_task(
};

if let Some(mutex_name) = &task.options.mutex {
if !ctx.named_mutexes.contains_key(mutex_name) {
ctx.named_mutexes
.insert(mutex_name.to_owned(), Arc::new(Mutex::new(())));
}

debug!(
target: LOG_TARGET,
"Waiting to acquire {} mutex lock for {} before running",
color::id(mutex_name),
color::label(&task.target),
);

if let Some(named_mutex) = ctx.named_mutexes.get(mutex_name) {
let _guard = named_mutex.lock().await;

debug!(
target: LOG_TARGET,
"Acquired {} mutex lock for {}",
color::id(mutex_name),
color::label(&task.target),
);

runner.create_and_run_command(&ctx, runtime).await
} else {
Result::Err(miette::Report::msg(format!(
Expand Down
1 change: 1 addition & 0 deletions crates/core/test-utils/src/configs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub fn get_cases_fixture_configs() -> (
("states".try_into().unwrap(), "states".to_owned()),
// Runner
("interactive".try_into().unwrap(), "interactive".to_owned()),
("mutex".try_into().unwrap(), "mutex".to_owned()),
(
"passthroughArgs".try_into().unwrap(),
"passthrough-args".to_owned(),
Expand Down
52 changes: 51 additions & 1 deletion packages/visualizer/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,61 @@
/* eslint-disable node/no-unsupported-features/node-builtins */

import './app.css';
import { useState } from 'preact/hooks';
import { Graph } from './components/Graph';

const SUPPORTED_LAYOUTS = ['dagre', 'klay', 'breadthfirst', 'grid'];

function getLayoutFromQuery(): string {
let layout = new URLSearchParams(window.location.search).get('layout');

if (!layout || !SUPPORTED_LAYOUTS.includes(layout)) {
layout = 'dagre';
}

return layout;
}

function setLayoutIntoQuery(layout: string) {
const query = new URLSearchParams(window.location.search);
query.set('layout', layout);

// Doesn't reload the page
window.history.pushState(null, '', `${window.location.pathname}?${query}`);
}

export function App() {
const [layout, setLayout] = useState(getLayoutFromQuery());

function handleChange(event: Event) {
const target = event.target as HTMLSelectElement;
const newLayout = target.value;

setLayout(newLayout);
setLayoutIntoQuery(newLayout);
}

return (
<main>
<div className="p-4 flex items-center float-right">
<span className="inline-block mr-1">Layout:</span>

<select
className="border border-slate-400 rounded bg-slate-600 text-slate-50 p-1"
value={layout}
onChange={handleChange}
>
{SUPPORTED_LAYOUTS.map((value) => (
<option key={value} value={value}>
{value}
</option>
))}
</select>
</div>

<h2 className="m-0 p-4 text-3xl font-extrabold sm:text-4xl">{window.PAGE_TITLE}</h2>
<Graph />

<Graph layout={layout} />
</main>
);
}
12 changes: 8 additions & 4 deletions packages/visualizer/src/components/Graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ import { useEffect, useRef } from 'preact/hooks';
import { render } from '../helpers/render';
import type { GraphInfo } from '../helpers/types';

export const Graph = () => {
export interface GraphProps {
layout: string;
}

export function Graph({ layout }: GraphProps) {
const graphRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (graphRef.current) {
render(graphRef.current, JSON.parse(window.GRAPH_DATA) as GraphInfo);
render(graphRef.current, JSON.parse(window.GRAPH_DATA) as GraphInfo, layout);
}
}, []);
}, [layout]);

return <div id="graph" ref={graphRef} style={{ height: '80vh', width: '100%' }} />;
};
}
13 changes: 2 additions & 11 deletions packages/visualizer/src/helpers/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import type { GraphInfo } from './types';
cytoscape.use(dagre);
cytoscape.use(klay);

const SUPPORTED_LAYOUTS = ['grid', 'dagre', 'klay', 'breadthfirst'];

function getActionType(label: string) {
if (label === 'SyncWorkspace') {
return 'sync-workspace';
Expand Down Expand Up @@ -44,7 +42,7 @@ function getShortDepLabel(label: string) {
return label;
}

export function render(element: HTMLElement, data: GraphInfo) {
export function render(element: HTMLElement, data: GraphInfo, layout: string) {
const nodes = data.nodes.map((n) => ({
data: { id: n.id.toString(), label: n.label, type: getActionType(n.label) },
}));
Expand All @@ -58,13 +56,6 @@ export function render(element: HTMLElement, data: GraphInfo) {
},
}));

// eslint-disable-next-line node/no-unsupported-features/node-builtins
let layout = new URLSearchParams(window.location.search).get('layout') ?? 'dagre';

if (layout && !SUPPORTED_LAYOUTS.includes(layout)) {
layout = 'dagre';
}

// https://js.cytoscape.org/
return cytoscape({
container: element,
Expand All @@ -86,7 +77,7 @@ export function render(element: HTMLElement, data: GraphInfo) {
label: 'data(label)',
'line-cap': 'round',
'line-color': '#c9eef6', // '#012a4a',
'line-opacity': 0.2,
'line-opacity': 0.18,
'overlay-color': '#c9eef6',
'target-arrow-color': '#c9eef6', // '#1a3f5c',
'target-arrow-shape': 'chevron',
Expand Down
15 changes: 15 additions & 0 deletions tests/fixtures/cases/mutex/moon.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
platform: 'node'

tasks:
run1:
command: 'node ./sleep.mjs'
options:
mutex: 'test-lock'
run2:
command: 'node ./sleep.mjs'
options:
mutex: 'test-lock'
run3:
command: 'node ./sleep.mjs'
options:
mutex: 'test-lock'
1 change: 1 addition & 0 deletions tests/fixtures/cases/mutex/sleep.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
await new Promise((resolve) => setTimeout(resolve, 3000));
47 changes: 43 additions & 4 deletions website/blog/2024-04-17_moon-v1.24.mdx
Original file line number Diff line number Diff line change
@@ -1,15 +1,53 @@
---
slug: moon-v1.24
title: moon v1.24 - ???
title: moon v1.24 - Task mutexes, auto-detect revisions, project dependents, and more!
authors: [milesj]
tags: []
# image: ./img/moon/v1.23.png
tags: [task, mutex, ci, base, head, project-graph, dependents]
image: ./img/moon/v1.24.png
---

???
This is a light release that focused solely in requests and improvements from the community.

<!--truncate-->

## Exclusive resources with task mutexes

Some tasks may require exclusive access to a resource, like a database, file, or network connection,
but other parallel running tasks may also require access to the same resource. When both of these
tasks run, they may conflict with each other, causing one or both to fail. This wasn't an easy
problem to solve in moon, until now!

Thanks to [Andrés Correa Casablanca](https://github.com/castarco) for the idea and implementation,
tasks now support a new option called [`mutex`](/docs/config/project#mutex). This option allows you
to specify a unique name (across the entire workspace) for the mutex, which will be used to lock the
task from running if another task with the same mutex name is already running.

Take the following for example:

```yaml
# server/moon.yml
tasks:
build:
command: 'build-server'
options:
mutex: 'app'

# client/moon.yml
tasks:
build:
command: 'build-client'
options:
mutex: 'app'
```

When [`moon run :build`](/docs/commands/run) or [`moon ci`](/docs/commands/ci) is ran, both tasks
will be parallelized within our pipeline, _but_ only one of them will run at a time. If the `server`
task is running, the `client` task will wait until the `server` task is complete. This ensures that
both tasks don't conflict with each other.

This could technically be solved through task `deps`, but with a mutex, it allows you to decouple
project and task dependencies, and instead focus on a virtual resource that is being locked.

## Auto-detect base/head for `moon ci`

We've updated [`moon ci`](/docs/commands/ci) to try and detect the base and head revisions
Expand Down Expand Up @@ -55,6 +93,7 @@ $ moon query projects --affected --dependents
View the [official release](https://github.com/moonrepo/moon/releases/tag/v1.24.0) for a full list
of changes.

- Added an experimental `moon templates` command, that lists all available codegen templates.
- Added a `runner.autoCleanCache` setting to `.moon/workspace.yml`, allowing the post-run clean
mechanism to be controlled.
- Updated `moon generate` with better argument to variable handling.
Expand Down
Binary file added website/blog/img/moon/v1.24.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 26580f0

Please sign in to comment.