Skip to content

Commit

Permalink
GEN-55: Move sim-engine from hashintel/hash repo into /labs (#29)
Browse files Browse the repository at this point in the history
* Add `engine`

* First attempt to run CI

* Attempt to fix CI

* `cargo fmt`

* Move to `sim-engine`

* Update apps/sim-engine/README.md

Co-authored-by: David @ HASH <[email protected]>

---------

Co-authored-by: David @ HASH <[email protected]>
  • Loading branch information
TimDiekmann and vilkinsons authored Sep 2, 2023
1 parent eab9370 commit b3cf90a
Show file tree
Hide file tree
Showing 1,630 changed files with 129,372 additions and 10 deletions.
42 changes: 42 additions & 0 deletions .github/actions/install-rust-toolchain/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Install Rust toolchain
description: "Install Rust toolchain"

inputs:
toolchain:
required: true
description: "Rust toolchain, e.g. 'stable' or 'nightly'"
working-directory:
description: "Working directory to run the action in"
required: false
default: "."

runs:
using: "composite"
steps:
- name: "Install Rust"
working-directory: ${{ inputs.working-directory }}
shell: bash
run: |
# regex for components that are only available on nightly and should not be installed on stable
# these are in variables due to: https://stackoverflow.com/a/56449915/9077988
NIGHTLY_ONLY="miri|llvm-tools|llvm-tools-preview"
NIGHTLY_TOOLCHAIN="nightly-.*"
# extract components from the rust-toolchain.toml
COMPONENTS=$(cat rust-toolchain.toml | yq -p toml '.toolchain.components[]')
rustup toolchain install "${{ inputs.toolchain }}"
for component in $COMPONENTS; do
# depending on the toolchain we need to conditionally skip specific components
if [[ ! "${{ inputs.toolchain }}" =~ $NIGHTLY_TOOLCHAIN ]]; then
# ensure that we only install components that are meant for stable
if [[ ! $component =~ $NIGHTLY_ONLY ]]; then
rustup component add --toolchain "${{ inputs.toolchain }}" "$component"
fi
else
rustup component add --toolchain "${{ inputs.toolchain }}" "$component"
fi
done
echo "RUSTUP_TOOLCHAIN=${{ inputs.toolchain }}" >> $GITHUB_ENV
16 changes: 16 additions & 0 deletions .github/actions/setup-rust-ci/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Setup Rust CI
description: Prepares Rust CI and installs Python to run scripts

runs:
using: composite
steps:
- name: Set up Python
uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0
with:
python-version: "3.10"
- name: Upgrade pip
shell: bash
run: python -m pip install --upgrade pip
- name: Install dependencies
shell: bash
run: pip install -r .github/scripts/rust/requirements.txt
2 changes: 2 additions & 0 deletions .github/scripts/rust/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pygit2 == 1.9.2
toml == 0.10.2
182 changes: 182 additions & 0 deletions .github/scripts/rust/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
"""
Setup script for the Rust GitHub Actions.
The output of this will be used as arguments for the GitHub Actions matrix.
see: https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs
"""

import re
import json
import itertools
import os
import toml

from fnmatch import fnmatch
from pathlib import Path
from pygit2 import Repository, Commit

CWD = Path.cwd()

# All jobs for all crates will run if any of these paths change
ALWAYS_RUN_PATTERNS = [".github/**", ".config/**", ".cargo/**"]

# We only run a subset of configurations for PRs, the rest will only be tested prior merging
IS_PULL_REQUEST_EVENT = "GITHUB_EVENT_NAME" in os.environ and os.environ["GITHUB_EVENT_NAME"] == "pull_request"


def generate_diffs():
"""
Generates a diff between `HEAD^` and `HEAD`
"""

repository = Repository(CWD)
head = repository.head.peel(Commit)
return repository.diff(head.parents[0], head, context_lines=0)


def find_local_crates():
"""
Returns all available crates in the workspace.
If a crate is in a sub-crate of another crate, only the super-crate will be returned because
the sub-crates will be picked up by `cargo` automatically.
:return: a list of crate paths
"""
return [path.relative_to(CWD).parent for path in CWD.rglob("Cargo.toml") if find_toolchain(path.parent)]


def find_toolchain(crate):
"""
Returns the toolchain for the specified crate.
The toolchain is determined by the `rust-toolchain.toml` file in the crate's directory or any parent directory
:param crate: the path to the crate
:return: the toolchain for the crate
"""
directory = crate
root = Path(directory.root)

while directory != root:
toolchain_file = directory / "rust-toolchain.toml"
if toolchain_file.exists():
toolchain = toml.load(toolchain_file).get("toolchain", {}).get("channel")
if toolchain:
return toolchain
directory = directory.parent

return None



def filter_parent_crates(crates):
checked_crates = []
for crate in crates:
if not any(path in crate.parents for path in crates):
checked_crates.append(crate)
return checked_crates


def filter_for_changed_crates(diffs, crates):
"""
Returns a list of paths to crates which have changed files
If a file was changed, which matches `ALWAYS_RUN_PATTERNS`, all crates will be returned
:param diffs: a list `Diff`s returned from git
:param crates: a list of paths to crates
:return: a list of crate paths
"""
# Check if any changed file matches `ALWAYS_RUN_PATTERNS`
if any(
fnmatch(diff.delta.new_file.path, pattern)
for diff in diffs
for pattern in ALWAYS_RUN_PATTERNS
):
return crates

# Get the unique crate paths which have changed files
return list(
{
crate
for crate in crates
for diff in diffs
if fnmatch(diff.delta.new_file.path, f"{crate}/**")
}
)


def output_matrix(name, github_output_file, crates, **kwargs):
"""
Outputs the job matrix for the given crates
:param name: The name where the list of crates will be stored to be read by GitHub Actions
:param crates: a list of paths to crates
"""

crate_names = {}
for crate in crates:
with open(
crate / "Cargo.toml", "r", encoding="UTF-8"
) as cargo_toml:
cargo_toml_obj = toml.loads(cargo_toml.read())
if "package" in cargo_toml_obj and "name" in cargo_toml_obj["package"]:
crate_names[crate] = cargo_toml_obj["package"]["name"]
else:
crate_names[crate] = str(crate.name.replace("_", "-"))

available_toolchains = set()
used_toolchain_combinations = []

for crate in crates:
toolchain = find_toolchain(crate)
available_toolchains.add(toolchain)
toolchains = [toolchain]

used_toolchain_combinations.append(
itertools.product([crate_names[crate]], toolchains, repeat=1)
)

available_toolchain_combinations = itertools.product(crate_names.values(), available_toolchains)
excluded_toolchain_combinations = set(available_toolchain_combinations).difference(
*used_toolchain_combinations
)

matrix = dict(
name=[crate_names[crate] for crate in crates],
toolchain=list(available_toolchains),
**kwargs,
exclude=[
dict(name=elem[0], toolchain=elem[1])
for elem in excluded_toolchain_combinations
],
include=[
dict(name=crate_names[crate], directory=str(crate))
for crate in crates
],
)

if len(matrix["name"]) == 0:
matrix = {}

github_output_file.write(f"{name}={json.dumps(matrix)}\n")
print(f"Job matrix for {name}: {json.dumps(matrix, indent=4)}")


def main():
diffs = generate_diffs()
available_crates = find_local_crates()
changed_crates = filter_for_changed_crates(diffs, available_crates)
changed_parent_crates = filter_parent_crates(changed_crates)

github_output_file = open(os.environ["GITHUB_OUTPUT_FILE_PATH"], "w")

output_matrix("lint", github_output_file, changed_parent_crates)
if IS_PULL_REQUEST_EVENT:
output_matrix("test", github_output_file, changed_parent_crates, profile=["dev"])
else:
output_matrix("test", github_output_file, changed_parent_crates, profile=["dev", "release"])

github_output_file.close()


if __name__ == "__main__":
main()
Loading

0 comments on commit b3cf90a

Please sign in to comment.