Compare commits

...

8 Commits

Author SHA1 Message Date
Lucien Greathouse
27af0c841b Release 6.0.0 2021-01-16 18:35:27 -07:00
Lucien Greathouse
cc4f4df4f9 Support changing ClassName in Reconciler.diff 2021-01-16 18:26:30 -07:00
Lucien Greathouse
5989ab3b85 Add project to test that plugin skips unwritable properties 2021-01-16 15:34:59 -07:00
Lucien Greathouse
040b9c1452 Delete accidentally committed test bash script 2021-01-14 11:55:24 -07:00
Lucien Greathouse
bb7bd2e27e plugin: Update reflection database 2021-01-13 23:31:57 -07:00
tacheometry
02dbd4ba75 fix hyphen (#378)
This uses an em dash instead of double lines (--)
2021-01-06 13:49:05 -07:00
Lucien Greathouse
3b149cc875 Drop SnapshotError in favor of anyhow::Error 2020-12-18 12:16:05 -08:00
Lucien Greathouse
f3745c68d2 Turn rbxm/rbxmx errors into error variants 2020-12-18 11:39:41 -08:00
25 changed files with 726 additions and 263 deletions

View File

@@ -2,6 +2,11 @@
## Unreleased Changes
## [6.0.0](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.0) (January 16, 2021)
* Improved server error messages
* The server will now keep running in more error cases
* Fixed Rojo being unable to diff ClassName changes
## [6.0.0 Release Candidate 4](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.0-rc.4) (December 14, 2020)
* Added brand new Rojo UI ([#367](https://github.com/rojo-rbx/rojo/pull/367))
* Added `projectName` to `/api/rojo` output.

2
Cargo.lock generated
View File

@@ -1961,7 +1961,7 @@ dependencies = [
[[package]]
name = "rojo"
version = "6.0.0-rc.4"
version = "6.0.0"
dependencies = [
"anyhow",
"backtrace",

View File

@@ -1,6 +1,6 @@
[package]
name = "rojo"
version = "6.0.0-rc.4"
version = "6.0.0"
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
description = "Enables professional-grade development tools for Roblox developers"
license = "MPL-2.0"

View File

@@ -1,10 +0,0 @@
#!/bin/sh
set -e
cargo build --release
echo "Known good:"
time rojo build ../uiblox/test-place.project.json -o UIBlox.rbxlx
echo "Current:"
time ./target/release/rojo build ../uiblox/test-place.project.json -o UIBlox.rbxlx

View File

@@ -8,7 +8,7 @@ Error.Kind = {
},
ConnectFailed = {
message = "Couldn't connect to the Rojo server.\n" ..
"Make sure the server is running -- use 'rojo serve' to run it!",
"Make sure the server is running use 'rojo serve' to run it!",
},
Timeout = {
message = "HTTP request timed out.",
@@ -63,4 +63,4 @@ function Error.fromRobloxErrorString(message)
return Error.new(Error.Kind.Unknown, message)
end
return Error
return Error

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@ local isDevBuild = script.Parent.Parent:FindFirstChild("ROJO_DEV_BUILD") ~= nil
return strict("Config", {
isDevBuild = isDevBuild,
codename = "Epiphany",
version = {6, 0, 0, "-rc.4"},
version = {6, 0, 0},
expectedServerVersionString = "6.0 or newer",
protocolVersion = 3,
defaultHost = "localhost",

View File

@@ -52,8 +52,9 @@ local function diff(instanceMap, virtualInstances, rootId)
invariant("Cannot diff an instance not present in InstanceMap\nID: {}", id)
end
local changedClassName = nil
if virtualInstance.ClassName ~= instance.ClassName then
error("unimplemented: support changing ClassName")
changedClassName = virtualInstance.ClassName
end
local changedName = nil
@@ -89,11 +90,11 @@ local function diff(instanceMap, virtualInstances, rootId)
end
end
if changedName ~= nil or not isEmpty(changedProperties) then
if changedName ~= nil or changedClassName ~= nil or not isEmpty(changedProperties) then
table.insert(patch.updated, {
id = id,
changedName = changedName,
changedClassName = nil,
changedClassName = changedClassName,
changedProperties = changedProperties,
changedMetadata = nil,
})

View File

@@ -292,7 +292,7 @@ fn compute_and_apply_changes(tree: &mut RojoTree, vfs: &Vfs, id: RbxId) -> Optio
return None;
}
Err(err) => {
log::error!("Snapshot error: {}", ErrorDisplay(err));
log::error!("Snapshot error: {:?}", err);
return None;
}
};
@@ -340,7 +340,7 @@ fn compute_and_apply_changes(tree: &mut RojoTree, vfs: &Vfs, id: RbxId) -> Optio
return None;
}
Err(err) => {
log::error!("{}", ErrorDisplay(err));
log::error!("{:?}", err);
return None;
}
};

View File

@@ -22,7 +22,7 @@ use crate::{
apply_patch_set, compute_patch_set, AppliedPatchSet, InstanceContext,
InstancePropertiesWithMeta, PatchSet, RojoTree,
},
snapshot_middleware::{snapshot_from_vfs, SnapshotError},
snapshot_middleware::snapshot_from_vfs,
};
/// Contains all of the state for a Rojo serve session.
@@ -234,8 +234,8 @@ pub enum ServeSessionError {
},
#[error(transparent)]
Snapshot {
Other {
#[from]
source: SnapshotError,
source: anyhow::Error,
},
}

View File

@@ -1,5 +1,6 @@
use std::{collections::BTreeMap, path::Path};
use anyhow::Context;
use maplit::hashmap;
use memofs::{IoResultExt, Vfs};
use rbx_dom_weak::RbxValue;
@@ -7,9 +8,7 @@ use serde::Serialize;
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
use super::{
error::SnapshotError, meta_file::AdjacentMetadata, middleware::SnapshotInstanceResult,
};
use super::{meta_file::AdjacentMetadata, middleware::SnapshotInstanceResult};
pub fn snapshot_csv(
_context: &InstanceContext,
@@ -20,8 +19,12 @@ pub fn snapshot_csv(
let meta_path = path.with_file_name(format!("{}.meta.json", instance_name));
let contents = vfs.read(path)?;
let table_contents = convert_localization_csv(&contents)
.map_err(|source| SnapshotError::malformed_l10n_csv(source, path))?;
let table_contents = convert_localization_csv(&contents).with_context(|| {
format!(
"File was not a valid LocalizationTable CSV file: {}",
path.display()
)
})?;
let mut snapshot = InstanceSnapshot::new()
.name(instance_name)

View File

@@ -4,10 +4,7 @@ use memofs::{DirEntry, IoResultExt, Vfs};
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
use super::{
error::SnapshotError, meta_file::DirectoryMetadata, middleware::SnapshotInstanceResult,
snapshot_from_vfs,
};
use super::{meta_file::DirectoryMetadata, middleware::SnapshotInstanceResult, snapshot_from_vfs};
pub fn snapshot_dir(context: &InstanceContext, vfs: &Vfs, path: &Path) -> SnapshotInstanceResult {
let passes_filter_rules = |child: &DirEntry| {
@@ -35,7 +32,7 @@ pub fn snapshot_dir(context: &InstanceContext, vfs: &Vfs, path: &Path) -> Snapsh
.file_name()
.expect("Could not extract file name")
.to_str()
.ok_or_else(|| SnapshotError::file_name_bad_unicode(path))?
.ok_or_else(|| anyhow::anyhow!("File name was not valid UTF-8: {}", path.display()))?
.to_string();
let meta_path = path.join("init.meta.json");

View File

@@ -1,101 +0,0 @@
use std::{io, path::PathBuf};
use thiserror::Error;
use crate::project::ProjectError;
#[derive(Debug, Error)]
pub enum SnapshotError {
#[error("file name had malformed Unicode")]
FileNameBadUnicode { path: PathBuf },
#[error("file had malformed Unicode contents at path {}", .path.display())]
FileContentsBadUnicode {
source: std::str::Utf8Error,
path: PathBuf,
},
#[error("malformed project file at path {}", .path.display())]
MalformedProject { source: ProjectError, path: PathBuf },
#[error("malformed .model.json file at path {}", .path.display())]
MalformedModelJson {
source: serde_json::Error,
path: PathBuf,
},
#[error("malformed .meta.json file at path {}", .path.display())]
MalformedMetaJson {
source: serde_json::Error,
path: PathBuf,
},
#[error("malformed JSON at path {}", .path.display())]
MalformedJson {
source: serde_json::Error,
path: PathBuf,
},
#[error("malformed CSV localization data at path {}", .path.display())]
MalformedLocalizationCsv { source: csv::Error, path: PathBuf },
#[error(transparent)]
Io {
#[from]
source: io::Error,
},
}
impl SnapshotError {
pub(crate) fn file_name_bad_unicode(path: impl Into<PathBuf>) -> Self {
Self::FileNameBadUnicode { path: path.into() }
}
pub(crate) fn file_contents_bad_unicode(
source: std::str::Utf8Error,
path: impl Into<PathBuf>,
) -> Self {
Self::FileContentsBadUnicode {
source,
path: path.into(),
}
}
pub(crate) fn malformed_project(source: ProjectError, path: impl Into<PathBuf>) -> Self {
Self::MalformedProject {
source,
path: path.into(),
}
}
pub(crate) fn malformed_model_json(
source: serde_json::Error,
path: impl Into<PathBuf>,
) -> Self {
Self::MalformedModelJson {
source,
path: path.into(),
}
}
pub(crate) fn malformed_meta_json(source: serde_json::Error, path: impl Into<PathBuf>) -> Self {
Self::MalformedMetaJson {
source,
path: path.into(),
}
}
pub(crate) fn malformed_json(source: serde_json::Error, path: impl Into<PathBuf>) -> Self {
Self::MalformedJson {
source,
path: path.into(),
}
}
pub(crate) fn malformed_l10n_csv(source: csv::Error, path: impl Into<PathBuf>) -> Self {
Self::MalformedLocalizationCsv {
source,
path: path.into(),
}
}
}

View File

@@ -1,5 +1,6 @@
use std::path::Path;
use anyhow::Context;
use maplit::hashmap;
use memofs::{IoResultExt, Vfs};
use rbx_dom_weak::RbxValue;
@@ -9,9 +10,7 @@ use crate::{
snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot},
};
use super::{
error::SnapshotError, meta_file::AdjacentMetadata, middleware::SnapshotInstanceResult,
};
use super::{meta_file::AdjacentMetadata, middleware::SnapshotInstanceResult};
pub fn snapshot_json(
context: &InstanceContext,
@@ -22,7 +21,7 @@ pub fn snapshot_json(
let contents = vfs.read(path)?;
let value: serde_json::Value = serde_json::from_slice(&contents)
.map_err(|err| SnapshotError::malformed_json(err, path))?;
.with_context(|| format!("File contains malformed JSON: {}", path.display()))?;
let as_lua = json_to_lua(value).to_string();

View File

@@ -1,5 +1,6 @@
use std::{borrow::Cow, collections::HashMap, path::Path};
use anyhow::Context;
use memofs::Vfs;
use rbx_dom_weak::UnresolvedRbxValue;
use rbx_reflection::try_resolve_value;
@@ -7,7 +8,7 @@ use serde::Deserialize;
use crate::snapshot::{InstanceContext, InstanceSnapshot};
use super::{error::SnapshotError, middleware::SnapshotInstanceResult};
use super::middleware::SnapshotInstanceResult;
pub fn snapshot_json_model(
context: &InstanceContext,
@@ -17,7 +18,7 @@ pub fn snapshot_json_model(
) -> SnapshotInstanceResult {
let contents = vfs.read(path)?;
let instance: JsonModel = serde_json::from_slice(&contents)
.map_err(|source| SnapshotError::malformed_model_json(source, path))?;
.with_context(|| format!("File is not a valid JSON model: {}", path.display()))?;
let mut snapshot = instance.core.into_snapshot(instance_name.to_owned());

View File

@@ -1,5 +1,6 @@
use std::{path::Path, str};
use anyhow::Context;
use maplit::hashmap;
use memofs::{IoResultExt, Vfs};
use rbx_dom_weak::RbxValue;
@@ -28,9 +29,8 @@ pub fn snapshot_lua(context: &InstanceContext, vfs: &Vfs, path: &Path) -> Snapsh
let contents = vfs.read(path)?;
let contents_str = str::from_utf8(&contents)
// TODO: Turn into error type
.expect("File content was not valid UTF-8")
.to_string();
.with_context(|| format!("File was not valid UTF-8: {}", path.display()))?
.to_owned();
let meta_path = path.with_file_name(format!("{}.meta.json", instance_name));
@@ -71,10 +71,14 @@ pub fn snapshot_lua_init(
let dir_snapshot = snapshot_dir(context, vfs, folder_path)?.unwrap();
if dir_snapshot.class_name != "Folder" {
panic!(
anyhow::bail!(
"init.lua, init.server.lua, and init.client.lua can \
only be used if the instance produced by the parent \
directory would be a Folder."
only be used if the instance produced by the containing \
directory would be a Folder.\n\n\
The directory {} turned into an instance of class {}.",
folder_path.display(),
dir_snapshot.class_name
);
}

View File

@@ -1,13 +1,12 @@
use std::{borrow::Cow, collections::HashMap, path::Path};
use anyhow::Context;
use rbx_dom_weak::UnresolvedRbxValue;
use rbx_reflection::try_resolve_value;
use serde::{Deserialize, Serialize};
use crate::snapshot::InstanceSnapshot;
use super::error::SnapshotError;
/// Represents metadata in a sibling file with the same basename.
///
/// As an example, hello.meta.json next to hello.lua would allow assigning
@@ -23,9 +22,13 @@ pub struct AdjacentMetadata {
}
impl AdjacentMetadata {
pub fn from_slice(slice: &[u8], path: &Path) -> Result<Self, SnapshotError> {
serde_json::from_slice(slice)
.map_err(|source| SnapshotError::malformed_meta_json(source, path))
pub fn from_slice(slice: &[u8], path: &Path) -> anyhow::Result<Self> {
serde_json::from_slice(slice).with_context(|| {
format!(
"File contained malformed .meta.json data: {}",
path.display()
)
})
}
pub fn apply_ignore_unknown_instances(&mut self, snapshot: &mut InstanceSnapshot) {
@@ -75,9 +78,13 @@ pub struct DirectoryMetadata {
}
impl DirectoryMetadata {
pub fn from_slice(slice: &[u8], path: &Path) -> Result<Self, SnapshotError> {
serde_json::from_slice(slice)
.map_err(|source| SnapshotError::malformed_meta_json(source, path))
pub fn from_slice(slice: &[u8], path: &Path) -> anyhow::Result<Self> {
serde_json::from_slice(slice).with_context(|| {
format!(
"File contained malformed init.meta.json data: {}",
path.display()
)
})
}
pub fn apply_all(&mut self, snapshot: &mut InstanceSnapshot) {

View File

@@ -1,5 +1,3 @@
use crate::snapshot::InstanceSnapshot;
use super::error::SnapshotError;
pub type SnapshotInstanceResult = Result<Option<InstanceSnapshot>, SnapshotError>;
pub type SnapshotInstanceResult = anyhow::Result<Option<InstanceSnapshot>>;

View File

@@ -5,7 +5,6 @@
mod csv;
mod dir;
mod error;
mod json;
mod json_model;
mod lua;
@@ -37,7 +36,6 @@ use self::{
util::match_file_name,
};
pub use self::error::*;
pub use self::project::snapshot_project_node;
pub fn snapshot_from_vfs(

View File

@@ -1,5 +1,6 @@
use std::{borrow::Cow, collections::HashMap, path::Path};
use anyhow::Context;
use memofs::Vfs;
use rbx_reflection::{get_class_descriptor, try_resolve_value};
@@ -10,7 +11,7 @@ use crate::{
},
};
use super::{error::SnapshotError, middleware::SnapshotInstanceResult, snapshot_from_vfs};
use super::{middleware::SnapshotInstanceResult, snapshot_from_vfs};
pub fn snapshot_project(
context: &InstanceContext,
@@ -18,7 +19,7 @@ pub fn snapshot_project(
path: &Path,
) -> SnapshotInstanceResult {
let project = Project::load_from_slice(&vfs.read(path)?, path)
.map_err(|err| SnapshotError::malformed_project(err, path))?;
.with_context(|| format!("File was not a valid Rojo project: {}", path.display()))?;
let mut context = context.clone();

View File

@@ -1,5 +1,6 @@
use std::{collections::HashMap, path::Path};
use anyhow::Context;
use memofs::Vfs;
use rbx_dom_weak::{RbxInstanceProperties, RbxTree};
@@ -21,7 +22,7 @@ pub fn snapshot_rbxm(
let root_id = temp_tree.get_root_id();
rbx_binary::decode(&mut temp_tree, root_id, vfs.read(path)?.as_slice())
.expect("TODO: Handle rbx_binary errors");
.with_context(|| format!("Malformed rbxm file: {}", path.display()))?;
let root_instance = temp_tree.get_instance(root_id).unwrap();
let children = root_instance.get_children_ids();
@@ -38,7 +39,11 @@ pub fn snapshot_rbxm(
Ok(Some(snapshot))
} else {
panic!("Rojo doesn't have support for model files with zero or more than one top-level instances yet.");
anyhow::bail!(
"Rojo doesn't have support for model files with zero or more than one top-level instances yet.\n\n \
Check the model file at path {}",
path.display()
);
}
}

View File

@@ -1,5 +1,6 @@
use std::path::Path;
use anyhow::Context;
use memofs::Vfs;
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
@@ -16,7 +17,7 @@ pub fn snapshot_rbxmx(
.property_behavior(rbx_xml::DecodePropertyBehavior::ReadUnknown);
let temp_tree = rbx_xml::from_reader(vfs.read(path)?.as_slice(), options)
.expect("TODO: Handle rbx_xml errors");
.with_context(|| format!("Malformed rbxm file: {}", path.display()))?;
let root_instance = temp_tree.get_instance(temp_tree.get_root_id()).unwrap();
let children = root_instance.get_children_ids();
@@ -33,7 +34,11 @@ pub fn snapshot_rbxmx(
Ok(Some(snapshot))
} else {
panic!("Rojo doesn't have support for model files with zero or more than one top-level instances yet.");
anyhow::bail!(
"Rojo doesn't have support for model files with zero or more than one top-level instances yet.\n\n \
Check the model file at path {}",
path.display()
);
}
}

View File

@@ -1,14 +1,13 @@
use std::{path::Path, str};
use anyhow::Context;
use maplit::hashmap;
use memofs::{IoResultExt, Vfs};
use rbx_dom_weak::RbxValue;
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
use super::{
error::SnapshotError, meta_file::AdjacentMetadata, middleware::SnapshotInstanceResult,
};
use super::{meta_file::AdjacentMetadata, middleware::SnapshotInstanceResult};
pub fn snapshot_txt(
context: &InstanceContext,
@@ -18,8 +17,8 @@ pub fn snapshot_txt(
) -> SnapshotInstanceResult {
let contents = vfs.read(path)?;
let contents_str = str::from_utf8(&contents)
.map_err(|err| SnapshotError::file_contents_bad_unicode(err, path))?
.to_string();
.with_context(|| format!("File was not valid UTF-8: {}", path.display()))?
.to_owned();
let properties = hashmap! {
"Value".to_owned() => RbxValue::String {

View File

@@ -0,0 +1,2 @@
# script-unwritable
This project is used to test that Rojo successfully builds places with properties that are unwritable to scripts, but are ignored when the same project is used for live sync.

View File

@@ -0,0 +1,18 @@
{
"name": "script-unwritable",
"tree": {
"$className": "DataModel",
"HttpService": {
"$properties": {
"HttpEnabled": true
}
},
"StarterPlayer": {
"$properties": {
"GameSettingsAvatar": "R15"
}
}
}
}