Skip to content

Commit

Permalink
Merge branch 'main' into kdy1/swc-core-86
Browse files Browse the repository at this point in the history
  • Loading branch information
kdy1 authored Nov 20, 2023
2 parents c6c128c + 922cc17 commit 70aec48
Show file tree
Hide file tree
Showing 83 changed files with 675 additions and 173 deletions.
53 changes: 52 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,55 @@ jobs:
runner: ubuntu-latest
- name: macos
runner: macos-latest
# Enable this once all tests on windows are passing (rememeber to copy over crlf fix from turborepo_integration_windows)
# - name: winodws
# runner: windows-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup-turborepo-environment
with:
windows: ${{ matrix.os.name == 'windows' }}
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Setup Graphviz
uses: ts-graphviz/setup-graphviz@v1
- name: Cache Prysk
id: cache-prysk
uses: actions/cache@v3
with:
path: cli/.cram_env
key: prysk-venv-${{ matrix.os.name }}

- name: Integration Tests
run: turbo run test --filter=turborepo-tests-integration --color --env-mode=strict --token=${{ secrets.TURBO_TOKEN }} --team=${{ vars.TURBO_TEAM }}
env:
EXPERIMENTAL_RUST_CODEPATH: true

# This is the same as turborepo_integration, but it runs on windows.
# Separate entry so it's not required for merge.
turborepo_integration_windows:
name: Turborepo Integration Tests (Windows)
needs: [determine_jobs, build_turborepo]
if: needs.determine_jobs.outputs.turborepo_integration == 'true'
timeout-minutes: 30
runs-on: ${{ matrix.os.runner }}
strategy:
fail-fast: false
matrix:
os:
- name: windows
runner: windows-latest
steps:
# On Windows, set autocrlf to input so that when the repo is cloned down
# the fixtures retain their line endings and don't get updated to CRLF.
# We want this because this repo also contains the fixtures for our test cases
# and these fixtures have files that need stable file hashes. If we let git update
# the line endings on checkout, the file hashes will change.
# https://www.git-scm.com/book/en/v2/Customizing-Git-Git-Configuration#_core_autocrlf
- name: set crlf
if: matrix.os.name == 'windows'
shell: bash
run: git config --global core.autocrlf input
- uses: actions/checkout@v3
- uses: ./.github/actions/setup-turborepo-environment
with:
windows: ${{ matrix.os.name == 'windows' }}
Expand Down Expand Up @@ -354,13 +401,17 @@ jobs:
# TODO: enable macos when --experimental-rust-codepath is passing all tests on ubuntu
# - name: macos
# runner: macos-latest
# TODO: Enable windows in a later pass
# - name: windows
# runner: windows-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup-turborepo-environment
with:
target: ${{ matrix.os.name }}
github-token: "${{ secrets.GITHUB_TOKEN }}"

- name: Setup Graphviz
uses: ts-graphviz/setup-graphviz@v1
- name: Cache Prysk
id: cache-prysk
uses: actions/cache@v3
Expand Down
1 change: 1 addition & 0 deletions cli/internal/graphvisualizer/graphvisualizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ func (g *GraphVisualizer) GenerateGraphFile(outputName string) error {
if err := g.generateMermaid(f); err != nil {
return err
}
g.ui.Output("")
g.ui.Output(fmt.Sprintf("✔ Generated task graph in %s", ui.Bold(outputFilename.ToString())))
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion crates/turborepo-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ async-stream = "0.3.4"
itertools = { workspace = true }
port_scanner = { workspace = true }
pretty_assertions = { workspace = true }
rand = { workspace = true }
tempdir = "0.3.7"
tempfile = { workspace = true }
test-case = { workspace = true }
Expand Down Expand Up @@ -64,6 +63,7 @@ notify = "5.1"
petgraph = { workspace = true }
pidlock = { path = "../turborepo-pidlock" }
prost = "0.11.6"
rand = { workspace = true }
reqwest = { workspace = true, default-features = false, features = ["json"] }
rustc_version_runtime = "0.2.1"
semver = { workspace = true }
Expand Down
82 changes: 82 additions & 0 deletions crates/turborepo-lib/src/engine/mermaid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use std::{collections::HashMap, io};

use itertools::Itertools;
use petgraph::{visit::EdgeRef, Graph};
use rand::{distributions::Uniform, prelude::Distribution, Rng, SeedableRng};

use super::{Built, Engine, TaskNode};

struct CapitalLetters;

impl Distribution<char> for CapitalLetters {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> char {
const RANGE: u32 = 26;
const GEN_ASCII_STR_CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let range = Uniform::new(0u32, GEN_ASCII_STR_CHARSET.len() as u32);
char::from_u32(GEN_ASCII_STR_CHARSET[range.sample(rng) as usize] as u32)
.expect("random number should be in bounds")
}
}

fn generate_id<R: Rng>(rng: &mut R) -> String {
CapitalLetters.sample_iter(rng).take(4).join("")
}

impl Engine<Built> {
pub fn mermaid_graph<W: io::Write>(&self, writer: W, is_single: bool) -> Result<(), io::Error> {
render_graph(writer, &self.task_graph, is_single)
}
}

fn render_graph<W: io::Write>(
mut writer: W,
graph: &Graph<TaskNode, ()>,
is_single: bool,
) -> Result<(), io::Error> {
// Chosen randomly.
// Pick a constant seed so that the same graph generates the same nodes every
// time. This is not a security-sensitive operation, it's just aliases for
// the graph nodes.
let mut rng = rand::rngs::SmallRng::seed_from_u64(4u64);

let display_node = match is_single {
true => |node: &TaskNode| match node {
TaskNode::Root => node.to_string(),
TaskNode::Task(task) => task.task().to_string(),
},
false => |node: &TaskNode| node.to_string(),
};

let mut edges = graph
.edge_references()
.map(|e| {
(
display_node(
graph
.node_weight(e.source())
.expect("node index should exist in graph"),
),
display_node(
graph
.node_weight(e.target())
.expect("node index should exist in graph"),
),
)
})
.collect::<Vec<_>>();
edges.sort();

writeln!(writer, "graph TD")?;
let mut name_cache = HashMap::<String, String>::new();
for (src, target) in edges {
let src_name = name_cache
.entry(src.clone())
.or_insert_with(|| generate_id(&mut rng));
write!(writer, "\t{src_name}(\"{src}\") --> ")?;
let target_name = name_cache
.entry(target.clone())
.or_insert_with(|| generate_id(&mut rng));
writeln!(writer, "{target_name}(\"{target}\")")?;
}
Ok(())
}
1 change: 1 addition & 0 deletions crates/turborepo-lib/src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod builder;
mod execute;

mod dot;
mod mermaid;

use std::{
collections::{HashMap, HashSet},
Expand Down
41 changes: 33 additions & 8 deletions crates/turborepo-lib/src/process/child.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ pub enum ChildState {
}

impl ChildState {
pub fn command_channel(&self) -> Option<&ChildCommandChannel> {
pub fn command_channel(&self) -> Option<ChildCommandChannel> {
match self {
ChildState::Running(c) => Some(c),
ChildState::Running(c) => Some(c.clone()),
ChildState::Exited(_) => None,
}
}
Expand Down Expand Up @@ -153,7 +153,7 @@ pub struct Child {
label: String,
}

#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct ChildCommandChannel(mpsc::Sender<ChildCommand>);

impl ChildCommandChannel {
Expand Down Expand Up @@ -222,7 +222,7 @@ impl Child {
// we received a command to stop the child process, or the channel was closed.
// in theory this happens when the last child is dropped, however in practice
// we will always get a `Permit` from the recv call before the channel can be
// dropped, and the cnannel is not closed while there are still permits
// dropped, and the channel is not closed while there are still permits
Some(ChildCommand::Stop) | None => {
debug!("stopping child process");
shutdown_style.process(&mut child).await
Expand Down Expand Up @@ -296,10 +296,13 @@ impl Child {
let mut watch = self.exit_channel.clone();

let fut = async {
let state = self.state.read().await;
let child = match state.command_channel() {
Some(child) => child,
None => return,
let child = {
let state = self.state.read().await;

match state.command_channel() {
Some(child) => child,
None => return,
}
};

// if this fails, it's because the channel is dropped (toctou)
Expand Down Expand Up @@ -435,6 +438,7 @@ impl Child {
mod test {
use std::{assert_matches::assert_matches, process::Stdio, time::Duration};

use futures::{stream::FuturesUnordered, StreamExt};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
process::Command,
Expand Down Expand Up @@ -764,4 +768,25 @@ mod test {

assert_matches!(exit, Some(ChildExit::Finished(None)));
}

#[tokio::test]
async fn test_multistop() {
let script = find_script_dir().join_component("hello_world.js");
let mut cmd = Command::new("node");
cmd.args([script.as_std_path()]);
let child = Child::spawn(cmd, ShutdownStyle::Kill).unwrap();

let mut stops = FuturesUnordered::new();
for _ in 1..10 {
let mut child = child.clone();
stops.push(async move {
child.stop().await;
});
}

while let Some(_) = tokio::time::timeout(Duration::from_secs(5), stops.next())
.await
.expect("timed out")
{}
}
}
8 changes: 3 additions & 5 deletions crates/turborepo-lib/src/run/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use thiserror::Error;
use turbopath::AbsoluteSystemPathBuf;
use turborepo_repository::package_graph;

use super::graph_visualizer;
use crate::{
config, daemon, engine, opts,
run::{global_hash, scope},
Expand All @@ -10,10 +10,8 @@ use crate::{

#[derive(Debug, Error)]
pub enum Error {
#[error("failed to open graph file {0}")]
OpenGraphFile(#[source] std::io::Error, AbsoluteSystemPathBuf),
#[error("failed to produce graph output")]
GraphOutput(#[source] std::io::Error),
#[error(transparent)]
Graph(#[from] graph_visualizer::Error),
#[error("error preparing engine: Invalid persistent task configuration:\n{0}")]
EngineValidation(String),
#[error(transparent)]
Expand Down
Loading

0 comments on commit 70aec48

Please sign in to comment.