forked from rojo-rbx/rojo
Compare commits
4 Commits
v7.6.0
...
plugin-tes
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47db569c52 | ||
|
|
1d3f8c8e9d | ||
|
|
a894313a4b | ||
|
|
7f73ae80dc |
23
.github/workflows/ci.yml
vendored
23
.github/workflows/ci.yml
vendored
@@ -60,7 +60,7 @@ jobs:
|
|||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.83.0
|
uses: dtolnay/rust-toolchain@1.79.0
|
||||||
|
|
||||||
- name: Restore Rust Cache
|
- name: Restore Rust Cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
@@ -83,6 +83,27 @@ jobs:
|
|||||||
target
|
target
|
||||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
test-plugin:
|
||||||
|
name: Test Plugin
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Setup Rokit
|
||||||
|
uses: CompeyDev/setup-rokit@v0.1.2
|
||||||
|
with:
|
||||||
|
version: 'v1.1.0'
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: lune run test-plugin
|
||||||
|
env:
|
||||||
|
RBX_API_KEY: ${{ secrets.PLUGIN_TEST_API_KEY }}
|
||||||
|
RBX_UNIVERSE_ID: ${{ vars.PLUGIN_TEST_UNIVERSE_ID }}
|
||||||
|
RBX_PLACE_ID: ${{ vars.PLUGIN_TEST_PLACE_ID }}
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
name: Rustfmt, Clippy, Stylua, & Selene
|
name: Rustfmt, Clippy, Stylua, & Selene
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -16,3 +16,6 @@
|
|||||||
[submodule "plugin/Packages/Highlighter"]
|
[submodule "plugin/Packages/Highlighter"]
|
||||||
path = plugin/Packages/Highlighter
|
path = plugin/Packages/Highlighter
|
||||||
url = https://github.com/boatbomber/highlighter.git
|
url = https://github.com/boatbomber/highlighter.git
|
||||||
|
[submodule ".lune/opencloud-execute"]
|
||||||
|
path = .lune/opencloud-execute
|
||||||
|
url = https://github.com/Dekkonot/opencloud-luau-execute-lune.git
|
||||||
|
|||||||
5
.lune/.luaurc
Normal file
5
.lune/.luaurc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"aliases": {
|
||||||
|
"lune": "~/.lune/.typedefs/0.10.2/"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
.lune/opencloud-execute
Submodule
1
.lune/opencloud-execute
Submodule
Submodule .lune/opencloud-execute added at 8ae86dd3ad
112
.lune/test-plugin.luau
Normal file
112
.lune/test-plugin.luau
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
local serde = require("@lune/serde")
|
||||||
|
local net = require("@lune/net")
|
||||||
|
local stdio = require("@lune/stdio")
|
||||||
|
local process = require("@lune/process")
|
||||||
|
local fs = require("@lune/fs")
|
||||||
|
|
||||||
|
local luau_execute = require("./opencloud-execute")
|
||||||
|
|
||||||
|
local TEST_SCRIPT = fs.readFile("plugin/run-tests.server.lua")
|
||||||
|
|
||||||
|
local PATH_VERSION_MATCH = "assets/%d+/versions/(.+)"
|
||||||
|
|
||||||
|
local UNIVERSE_ID = process.env["RBX_UNIVERSE_ID"]
|
||||||
|
local PLACE_ID = process.env["RBX_PLACE_ID"]
|
||||||
|
local API_KEY = process.env["RBX_API_KEY"]
|
||||||
|
|
||||||
|
if not UNIVERSE_ID then
|
||||||
|
error("no universe ID specified. try providing one with the env var `RBX_UNIVERSE_ID`")
|
||||||
|
end
|
||||||
|
if not PLACE_ID then
|
||||||
|
error("no place ID specified. try providing one with the env var `RBX_PLACE_ID`")
|
||||||
|
end
|
||||||
|
if not API_KEY then
|
||||||
|
error("no API key specified. try providing one with the env var `RBX_API_KEY`")
|
||||||
|
end
|
||||||
|
|
||||||
|
--stylua: ignore
|
||||||
|
local upload_result = process.exec("cargo", {
|
||||||
|
"run", "--",
|
||||||
|
"upload", "plugin/test-place.project.json",
|
||||||
|
"--api_key", API_KEY,
|
||||||
|
"--universe_id", UNIVERSE_ID,
|
||||||
|
"--asset_id", PLACE_ID
|
||||||
|
}, {
|
||||||
|
stdio = "none"
|
||||||
|
})
|
||||||
|
|
||||||
|
if not upload_result.ok then
|
||||||
|
print("Failed to upload plugin test place")
|
||||||
|
print("Not dumping stdout or stderr to avoid leaking secrets")
|
||||||
|
process.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- This is /probably/ not necessary because Rojo generally does not have enough
|
||||||
|
-- activity that there will be multiple CI runs happening at once, but
|
||||||
|
-- it's better safe than sorry.
|
||||||
|
local version_response = net.request({
|
||||||
|
method = "GET",
|
||||||
|
url = `https://apis.roblox.com/assets/v1/assets/{PLACE_ID}/versions`,
|
||||||
|
query = {
|
||||||
|
maxPageSize = 1,
|
||||||
|
},
|
||||||
|
headers = {
|
||||||
|
["User-Agent"] = `Rojo/PluginTesting 1.0.0; {_VERSION}`,
|
||||||
|
["x-api-key"] = API_KEY,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if not version_response.ok then
|
||||||
|
error(
|
||||||
|
`Failed to fetch version of Roblox place to run tests on because: {version_response.statusCode} - {version_response.statusMessage}\n{version_response.body}`
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
local place_version_raw = serde.decode("json", version_response.body).assetVersions[1].path
|
||||||
|
assert(typeof(place_version_raw) == "string", "the result from asset version endpoint was not as expected")
|
||||||
|
|
||||||
|
local place_version = string.match(place_version_raw, PATH_VERSION_MATCH)
|
||||||
|
|
||||||
|
local task = luau_execute.create_task_versioned(UNIVERSE_ID, PLACE_ID, place_version, TEST_SCRIPT)
|
||||||
|
print(`Running test script on {UNIVERSE_ID}/{PLACE_ID}@{place_version}`)
|
||||||
|
print(`Task ID: {luau_execute.task_id(task)}`)
|
||||||
|
|
||||||
|
luau_execute.await_finish(task)
|
||||||
|
print("Output from task:\n")
|
||||||
|
local logs = luau_execute.get_structured_logs(task)
|
||||||
|
for _, log in logs do
|
||||||
|
if log.messageType == "OUTPUT" or log.messageType == "MESSAGE_TYPE_UNSPECIFIED" then
|
||||||
|
stdio.write(stdio.color("reset"))
|
||||||
|
elseif log.messageType == "INFO" then
|
||||||
|
stdio.write(stdio.color("cyan"))
|
||||||
|
elseif log.messageType == "WARNING" then
|
||||||
|
stdio.write(stdio.color("yellow"))
|
||||||
|
elseif log.messageType == "ERROR" then
|
||||||
|
stdio.write(stdio.color("red"))
|
||||||
|
end
|
||||||
|
stdio.write(log.message)
|
||||||
|
stdio.write(`{stdio.color("reset")}\n`)
|
||||||
|
end
|
||||||
|
|
||||||
|
local results = luau_execute.get_output(task)[1]
|
||||||
|
if not results then
|
||||||
|
error("plugin tests did not return any results")
|
||||||
|
end
|
||||||
|
|
||||||
|
local status = luau_execute.check_status(task)
|
||||||
|
if status == "COMPLETE" then
|
||||||
|
if results.failureCount == 0 then
|
||||||
|
process.exit(0)
|
||||||
|
else
|
||||||
|
process.exit(1)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
print()
|
||||||
|
print("Task did not finish successfully")
|
||||||
|
local err = luau_execute.get_error(task)
|
||||||
|
if err then
|
||||||
|
print(`Error from task: {err.code}`)
|
||||||
|
print(err.message)
|
||||||
|
end
|
||||||
|
process.exit(1)
|
||||||
|
end
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# Rojo Changelog
|
# Rojo Changelog
|
||||||
|
|
||||||
## 7.6.0 - October 10th, 2025
|
## Unreleased
|
||||||
* Added flag to `rojo init` to skip initializing a git repository ([#1122])
|
|
||||||
* Added fallback method for when an Instance can't be synced through normal means ([#1030])
|
* Added fallback method for when an Instance can't be synced through normal means ([#1030])
|
||||||
This should make it possible to sync `MeshParts` and `Unions`!
|
This should make it possible to sync `MeshParts` and `Unions`!
|
||||||
|
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
* Added `--absolute` flag to the sourcemap subcommand, which will emit absolute paths instead of relative paths. ([#1092])
|
* Added `--absolute` flag to the sourcemap subcommand, which will emit absolute paths instead of relative paths. ([#1092])
|
||||||
* Fixed applying `gameId` and `placeId` before initial sync was accepted ([#1104])
|
* Fixed applying `gameId` and `placeId` before initial sync was accepted ([#1104])
|
||||||
|
|
||||||
[#1122]: https://github.com/rojo-rbx/rojo/pull/1122
|
|
||||||
[#1030]: https://github.com/rojo-rbx/rojo/pull/1030
|
[#1030]: https://github.com/rojo-rbx/rojo/pull/1030
|
||||||
[#1096]: https://github.com/rojo-rbx/rojo/pull/1096
|
[#1096]: https://github.com/rojo-rbx/rojo/pull/1096
|
||||||
[#1093]: https://github.com/rojo-rbx/rojo/pull/1093
|
[#1093]: https://github.com/rojo-rbx/rojo/pull/1093
|
||||||
|
|||||||
87
Cargo.lock
generated
87
Cargo.lock
generated
@@ -430,16 +430,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
|
checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 0.1.10",
|
"cfg-if 0.1.10",
|
||||||
"dirs-sys 0.3.7",
|
"dirs-sys",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dirs"
|
|
||||||
version = "5.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
|
||||||
dependencies = [
|
|
||||||
"dirs-sys 0.4.1",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -453,18 +444,6 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dirs-sys"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"option-ext",
|
|
||||||
"redox_users",
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.10.0"
|
version = "1.10.0"
|
||||||
@@ -1114,12 +1093,23 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lz4_flex"
|
name = "lz4"
|
||||||
version = "0.11.5"
|
version = "1.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a"
|
checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"twox-hash",
|
"libc",
|
||||||
|
"lz4-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lz4-sys"
|
||||||
|
version = "1.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1311,12 +1301,6 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "option-ext"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "6.6.1"
|
version = "6.6.1"
|
||||||
@@ -1615,13 +1599,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rbx_binary"
|
name = "rbx_binary"
|
||||||
version = "2.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d419f67c8012bf83569086e1208c541478b3b8e4f523deaa0b80d723fb5ef22"
|
checksum = "9573fee5e073d7b303f475c285197fdc8179468de66ca60ee115a58fbac99296"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"log",
|
"log",
|
||||||
"lz4_flex",
|
"lz4",
|
||||||
"profiling",
|
"profiling",
|
||||||
"rbx_dom_weak",
|
"rbx_dom_weak",
|
||||||
"rbx_reflection",
|
"rbx_reflection",
|
||||||
@@ -1632,9 +1616,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rbx_dom_weak"
|
name = "rbx_dom_weak"
|
||||||
version = "4.0.0"
|
version = "3.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc74878a4a801afc8014b14ede4b38015a13de5d29ab0095d5ed284a744253f6"
|
checksum = "04425cf6e9376e5486f4fb35906c120d1b1b45618a490318cf563fab1fa230a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"rbx_types",
|
"rbx_types",
|
||||||
@@ -1644,9 +1628,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rbx_reflection"
|
name = "rbx_reflection"
|
||||||
version = "6.0.0"
|
version = "5.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "565dd3430991f35443fa6d23cc239fade2110c5089deb6bae5de77c400df4fd2"
|
checksum = "1b6d0d62baa613556b058a5f94a53b01cf0ccde0ea327ce03056e335b982e77e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rbx_types",
|
"rbx_types",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -1655,12 +1639,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rbx_reflection_database"
|
name = "rbx_reflection_database"
|
||||||
version = "2.0.0+roblox-694"
|
version = "1.0.3+roblox-670"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "844ceb61f23bad59b06d7299b69ff276579316eafa9857981da3012a6223f663"
|
checksum = "e22c05ef92528c0fb0cc580592a65ca178d3ea9beb07a1d9ca0a2503c4f3721c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs 5.0.1",
|
"lazy_static",
|
||||||
"log",
|
|
||||||
"rbx_reflection",
|
"rbx_reflection",
|
||||||
"rmp-serde",
|
"rmp-serde",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -1668,9 +1651,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rbx_types"
|
name = "rbx_types"
|
||||||
version = "3.0.0"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03220ffce2bd06ad04f77a003cb807f2e5b2a18e97623066a5ac735a978398af"
|
checksum = "78e4fdde46493def107e5f923d82e813dec9b3eef52c2f75fbad3a716023eda2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.13.1",
|
"base64 0.13.1",
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
@@ -1683,9 +1666,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rbx_xml"
|
name = "rbx_xml"
|
||||||
version = "2.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "be6c302cefe9c92ed09bcbb075cd24379271de135b0af331409a64c2ea3646ee"
|
checksum = "bb623833c31cc43bbdaeb32f5e91db8ecd63fc46e438d0d268baf9e61539cf1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"base64 0.13.1",
|
"base64 0.13.1",
|
||||||
@@ -1877,14 +1860,14 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "743bb8c693a387f1ae8d2026d82d8b0c175cc4777b97c1f7b12fdb3be595bb13"
|
checksum = "743bb8c693a387f1ae8d2026d82d8b0c175cc4777b97c1f7b12fdb3be595bb13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs 2.0.2",
|
"dirs",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"winreg 0.6.2",
|
"winreg 0.6.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rojo"
|
name = "rojo"
|
||||||
version = "7.6.0"
|
version = "7.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"backtrace",
|
"backtrace",
|
||||||
@@ -2471,12 +2454,6 @@ version = "0.2.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "twox-hash"
|
|
||||||
version = "2.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.17.0"
|
version = "1.17.0"
|
||||||
|
|||||||
14
Cargo.toml
14
Cargo.toml
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rojo"
|
name = "rojo"
|
||||||
version = "7.6.0"
|
version = "7.5.1"
|
||||||
rust-version = "1.83"
|
rust-version = "1.79.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Lucien Greathouse <me@lpghatguy.com>",
|
"Lucien Greathouse <me@lpghatguy.com>",
|
||||||
"Micah Reid <git@dekkonot.com>",
|
"Micah Reid <git@dekkonot.com>",
|
||||||
@@ -55,11 +55,11 @@ memofs = { version = "0.3.0", path = "crates/memofs" }
|
|||||||
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
|
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
|
||||||
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
||||||
|
|
||||||
rbx_binary = "2.0.0"
|
rbx_binary = "1.0.0"
|
||||||
rbx_dom_weak = "4.0.0"
|
rbx_dom_weak = "3.0.0"
|
||||||
rbx_reflection = "6.0.0"
|
rbx_reflection = "5.0.0"
|
||||||
rbx_reflection_database = "2.0.0"
|
rbx_reflection_database = "1.0.3"
|
||||||
rbx_xml = "2.0.0"
|
rbx_xml = "1.0.0"
|
||||||
|
|
||||||
anyhow = "1.0.80"
|
anyhow = "1.0.80"
|
||||||
backtrace = "0.3.69"
|
backtrace = "0.3.69"
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ Check out our [contribution guide](CONTRIBUTING.md) for detailed instructions fo
|
|||||||
|
|
||||||
Pull requests are welcome!
|
Pull requests are welcome!
|
||||||
|
|
||||||
Rojo supports Rust 1.83 and newer. The minimum supported version of Rust is based on the latest versions of the dependencies that Rojo has.
|
Rojo supports Rust 1.70.0 and newer. The minimum supported version of Rust is based on the latest versions of the dependencies that Rojo has.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details.
|
Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details.
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
print("Hello world, from client!")
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
print("Hello world, from server!")
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
return function()
|
|
||||||
print("Hello, world!")
|
|
||||||
end
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
print("Hello world, from plugin!")
|
|
||||||
12
build.rs
12
build.rs
@@ -47,7 +47,6 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
|
|
||||||
let root_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
let root_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
||||||
let plugin_dir = root_dir.join("plugin");
|
let plugin_dir = root_dir.join("plugin");
|
||||||
let templates_dir = root_dir.join("assets").join("project-templates");
|
|
||||||
|
|
||||||
let our_version = Version::parse(env::var_os("CARGO_PKG_VERSION").unwrap().to_str().unwrap())?;
|
let our_version = Version::parse(env::var_os("CARGO_PKG_VERSION").unwrap().to_str().unwrap())?;
|
||||||
let plugin_version =
|
let plugin_version =
|
||||||
@@ -58,9 +57,7 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
"plugin version does not match Cargo version"
|
"plugin version does not match Cargo version"
|
||||||
);
|
);
|
||||||
|
|
||||||
let template_snapshot = snapshot_from_fs_path(&templates_dir)?;
|
let snapshot = VfsSnapshot::dir(hashmap! {
|
||||||
|
|
||||||
let plugin_snapshot = VfsSnapshot::dir(hashmap! {
|
|
||||||
"default.project.json" => snapshot_from_fs_path(&root_dir.join("plugin.project.json"))?,
|
"default.project.json" => snapshot_from_fs_path(&root_dir.join("plugin.project.json"))?,
|
||||||
"plugin" => VfsSnapshot::dir(hashmap! {
|
"plugin" => VfsSnapshot::dir(hashmap! {
|
||||||
"fmt" => snapshot_from_fs_path(&plugin_dir.join("fmt"))?,
|
"fmt" => snapshot_from_fs_path(&plugin_dir.join("fmt"))?,
|
||||||
@@ -73,11 +70,10 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
let template_file = File::create(Path::new(&out_dir).join("templates.bincode"))?;
|
let out_path = Path::new(&out_dir).join("plugin.bincode");
|
||||||
let plugin_file = File::create(Path::new(&out_dir).join("plugin.bincode"))?;
|
let out_file = File::create(out_path)?;
|
||||||
|
|
||||||
bincode::serialize_into(plugin_file, &plugin_snapshot)?;
|
bincode::serialize_into(out_file, &snapshot)?;
|
||||||
bincode::serialize_into(template_file, &template_snapshot)?;
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build/windows/rojo-manifest.rc");
|
println!("cargo:rerun-if-changed=build/windows/rojo-manifest.rc");
|
||||||
println!("cargo:rerun-if-changed=build/windows/rojo.manifest");
|
println!("cargo:rerun-if-changed=build/windows/rojo.manifest");
|
||||||
|
|||||||
@@ -228,17 +228,23 @@ impl VfsBackend for InMemoryFs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn must_be_file<T>(path: &Path) -> io::Result<T> {
|
fn must_be_file<T>(path: &Path) -> io::Result<T> {
|
||||||
Err(io::Error::other(format!(
|
Err(io::Error::new(
|
||||||
"path {} was a directory, but must be a file",
|
io::ErrorKind::Other,
|
||||||
path.display()
|
format!(
|
||||||
)))
|
"path {} was a directory, but must be a file",
|
||||||
|
path.display()
|
||||||
|
),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn must_be_dir<T>(path: &Path) -> io::Result<T> {
|
fn must_be_dir<T>(path: &Path) -> io::Result<T> {
|
||||||
Err(io::Error::other(format!(
|
Err(io::Error::new(
|
||||||
"path {} was a file, but must be a directory",
|
io::ErrorKind::Other,
|
||||||
path.display()
|
format!(
|
||||||
)))
|
"path {} was a file, but must be a directory",
|
||||||
|
path.display()
|
||||||
|
),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn not_found<T>(path: &Path) -> io::Result<T> {
|
fn not_found<T>(path: &Path) -> io::Result<T> {
|
||||||
|
|||||||
@@ -15,27 +15,45 @@ impl NoopBackend {
|
|||||||
|
|
||||||
impl VfsBackend for NoopBackend {
|
impl VfsBackend for NoopBackend {
|
||||||
fn read(&mut self, _path: &Path) -> io::Result<Vec<u8>> {
|
fn read(&mut self, _path: &Path) -> io::Result<Vec<u8>> {
|
||||||
Err(io::Error::other("NoopBackend doesn't do anything"))
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, _path: &Path, _data: &[u8]) -> io::Result<()> {
|
fn write(&mut self, _path: &Path, _data: &[u8]) -> io::Result<()> {
|
||||||
Err(io::Error::other("NoopBackend doesn't do anything"))
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_dir(&mut self, _path: &Path) -> io::Result<ReadDir> {
|
fn read_dir(&mut self, _path: &Path) -> io::Result<ReadDir> {
|
||||||
Err(io::Error::other("NoopBackend doesn't do anything"))
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_file(&mut self, _path: &Path) -> io::Result<()> {
|
fn remove_file(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
Err(io::Error::other("NoopBackend doesn't do anything"))
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_dir_all(&mut self, _path: &Path) -> io::Result<()> {
|
fn remove_dir_all(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
Err(io::Error::other("NoopBackend doesn't do anything"))
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn metadata(&mut self, _path: &Path) -> io::Result<Metadata> {
|
fn metadata(&mut self, _path: &Path) -> io::Result<Metadata> {
|
||||||
Err(io::Error::other("NoopBackend doesn't do anything"))
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
||||||
@@ -43,11 +61,17 @@ impl VfsBackend for NoopBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn watch(&mut self, _path: &Path) -> io::Result<()> {
|
fn watch(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
Err(io::Error::other("NoopBackend doesn't do anything"))
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwatch(&mut self, _path: &Path) -> io::Result<()> {
|
fn unwatch(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
Err(io::Error::other("NoopBackend doesn't do anything"))
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"NoopBackend doesn't do anything",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,13 +109,15 @@ impl VfsBackend for StdBackend {
|
|||||||
self.watches.insert(path.to_path_buf());
|
self.watches.insert(path.to_path_buf());
|
||||||
self.watcher
|
self.watcher
|
||||||
.watch(path, RecursiveMode::Recursive)
|
.watch(path, RecursiveMode::Recursive)
|
||||||
.map_err(io::Error::other)
|
.map_err(|inner| io::Error::new(io::ErrorKind::Other, inner))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwatch(&mut self, path: &Path) -> io::Result<()> {
|
fn unwatch(&mut self, path: &Path) -> io::Result<()> {
|
||||||
self.watches.remove(path);
|
self.watches.remove(path);
|
||||||
self.watcher.unwatch(path).map_err(io::Error::other)
|
self.watcher
|
||||||
|
.unwatch(path)
|
||||||
|
.map_err(|inner| io::Error::new(io::ErrorKind::Other, inner))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
7.6.0
|
7.5.1
|
||||||
@@ -378,26 +378,13 @@ types = {
|
|||||||
if pod == "Default" then
|
if pod == "Default" then
|
||||||
return nil
|
return nil
|
||||||
else
|
else
|
||||||
-- Passing `nil` instead of not passing anything gives
|
return PhysicalProperties.new(
|
||||||
-- different results, so we have to branch here.
|
pod.density,
|
||||||
if pod.acousticAbsorption then
|
pod.friction,
|
||||||
return (PhysicalProperties.new :: any)(
|
pod.elasticity,
|
||||||
pod.density,
|
pod.frictionWeight,
|
||||||
pod.friction,
|
pod.elasticityWeight
|
||||||
pod.elasticity,
|
)
|
||||||
pod.frictionWeight,
|
|
||||||
pod.elasticityWeight,
|
|
||||||
pod.acousticAbsorption
|
|
||||||
)
|
|
||||||
else
|
|
||||||
return PhysicalProperties.new(
|
|
||||||
pod.density,
|
|
||||||
pod.friction,
|
|
||||||
pod.elasticity,
|
|
||||||
pod.frictionWeight,
|
|
||||||
pod.elasticityWeight
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
@@ -411,7 +398,6 @@ types = {
|
|||||||
elasticity = roblox.Elasticity,
|
elasticity = roblox.Elasticity,
|
||||||
frictionWeight = roblox.FrictionWeight,
|
frictionWeight = roblox.FrictionWeight,
|
||||||
elasticityWeight = roblox.ElasticityWeight,
|
elasticityWeight = roblox.ElasticityWeight,
|
||||||
acousticAbsorption = roblox.AcousticAbsorption,
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|||||||
@@ -441,8 +441,7 @@
|
|||||||
"friction": 1.0,
|
"friction": 1.0,
|
||||||
"elasticity": 0.0,
|
"elasticity": 0.0,
|
||||||
"frictionWeight": 50.0,
|
"frictionWeight": 50.0,
|
||||||
"elasticityWeight": 25.0,
|
"elasticityWeight": 25.0
|
||||||
"acousticAbsorption": 0.15625
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ty": "PhysicalProperties"
|
"ty": "PhysicalProperties"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -8,4 +8,12 @@ local Settings = require(Rojo.Plugin.Settings)
|
|||||||
Settings:set("logLevel", "Trace")
|
Settings:set("logLevel", "Trace")
|
||||||
Settings:set("typecheckingEnabled", true)
|
Settings:set("typecheckingEnabled", true)
|
||||||
|
|
||||||
require(Rojo.Plugin.runTests)(TestEZ)
|
local results = require(Rojo.Plugin.runTests)(TestEZ)
|
||||||
|
|
||||||
|
-- Roblox's Luau execution gets mad about cyclical tables.
|
||||||
|
-- Rather than making TestEZ not do that, we just send back the important info.
|
||||||
|
return {
|
||||||
|
failureCount = results.failureCount,
|
||||||
|
successCount = results.successCount,
|
||||||
|
skippedCount = results.skippedCount,
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ return function(TestEZ)
|
|||||||
local Rojo = script.Parent.Parent
|
local Rojo = script.Parent.Parent
|
||||||
local Packages = Rojo.Packages
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
TestEZ.TestBootstrap:run({ Rojo.Plugin, Packages.Http, Packages.Log, Packages.RbxDom })
|
return TestEZ.TestBootstrap:run({ Rojo.Plugin, Packages.Http, Packages.Log, Packages.RbxDom })
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ rojo = "rojo-rbx/rojo@7.5.1"
|
|||||||
selene = "Kampfkarren/selene@0.29.0"
|
selene = "Kampfkarren/selene@0.29.0"
|
||||||
stylua = "JohnnyMorganz/stylua@2.1.0"
|
stylua = "JohnnyMorganz/stylua@2.1.0"
|
||||||
run-in-roblox = "rojo-rbx/run-in-roblox@0.3.0"
|
run-in-roblox = "rojo-rbx/run-in-roblox@0.3.0"
|
||||||
|
lune = "lune-org/lune@0.10.2"
|
||||||
|
|||||||
242
src/cli/init.rs
242
src/cli/init.rs
@@ -1,49 +1,45 @@
|
|||||||
|
use std::io::{self, Write};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{
|
|
||||||
collections::VecDeque,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
ffi::OsStr,
|
|
||||||
io::{self, Write},
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::{bail, format_err};
|
use anyhow::{bail, format_err};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
use fs_err::OpenOptions;
|
use fs_err::OpenOptions;
|
||||||
use memofs::{InMemoryFs, Vfs, VfsSnapshot};
|
|
||||||
|
|
||||||
use super::resolve_path;
|
use super::resolve_path;
|
||||||
|
|
||||||
const GIT_IGNORE_PLACEHOLDER: &str = "gitignore.txt";
|
static MODEL_PROJECT: &str =
|
||||||
|
include_str!("../../assets/default-model-project/default.project.json");
|
||||||
|
static MODEL_README: &str = include_str!("../../assets/default-model-project/README.md");
|
||||||
|
static MODEL_INIT: &str = include_str!("../../assets/default-model-project/src-init.luau");
|
||||||
|
static MODEL_GIT_IGNORE: &str = include_str!("../../assets/default-model-project/gitignore.txt");
|
||||||
|
|
||||||
static TEMPLATE_BINCODE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/templates.bincode"));
|
static PLACE_PROJECT: &str =
|
||||||
|
include_str!("../../assets/default-place-project/default.project.json");
|
||||||
|
static PLACE_README: &str = include_str!("../../assets/default-place-project/README.md");
|
||||||
|
static PLACE_GIT_IGNORE: &str = include_str!("../../assets/default-place-project/gitignore.txt");
|
||||||
|
|
||||||
|
static PLUGIN_PROJECT: &str =
|
||||||
|
include_str!("../../assets/default-plugin-project/default.project.json");
|
||||||
|
static PLUGIN_README: &str = include_str!("../../assets/default-plugin-project/README.md");
|
||||||
|
static PLUGIN_GIT_IGNORE: &str = include_str!("../../assets/default-plugin-project/gitignore.txt");
|
||||||
|
|
||||||
/// Initializes a new Rojo project.
|
/// Initializes a new Rojo project.
|
||||||
///
|
|
||||||
/// By default, this will attempt to initialize a 'git' repository in the
|
|
||||||
/// project directory if `git` is installed. To avoid this, pass `--skip-git`.
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct InitCommand {
|
pub struct InitCommand {
|
||||||
/// Path to the place to create the project. Defaults to the current directory.
|
/// Path to the place to create the project. Defaults to the current directory.
|
||||||
#[clap(default_value = "")]
|
#[clap(default_value = "")]
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
|
|
||||||
/// The kind of project to create, 'place', 'plugin', or 'model'.
|
/// The kind of project to create, 'place', 'plugin', or 'model'. Defaults to place.
|
||||||
#[clap(long, default_value = "place")]
|
#[clap(long, default_value = "place")]
|
||||||
pub kind: InitKind,
|
pub kind: InitKind,
|
||||||
|
|
||||||
/// Skips the initialization of a git repository.
|
|
||||||
#[clap(long)]
|
|
||||||
pub skip_git: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InitCommand {
|
impl InitCommand {
|
||||||
pub fn run(self) -> anyhow::Result<()> {
|
pub fn run(self) -> anyhow::Result<()> {
|
||||||
let template = self.kind.template();
|
|
||||||
|
|
||||||
let base_path = resolve_path(&self.path);
|
let base_path = resolve_path(&self.path);
|
||||||
fs::create_dir_all(&base_path)?;
|
fs::create_dir_all(&base_path)?;
|
||||||
|
|
||||||
@@ -57,51 +53,10 @@ impl InitCommand {
|
|||||||
name: project_name.to_owned(),
|
name: project_name.to_owned(),
|
||||||
};
|
};
|
||||||
|
|
||||||
println!(
|
match self.kind {
|
||||||
"Creating new {:?} project '{}'",
|
InitKind::Place => init_place(&base_path, project_params)?,
|
||||||
self.kind, project_params.name
|
InitKind::Model => init_model(&base_path, project_params)?,
|
||||||
);
|
InitKind::Plugin => init_plugin(&base_path, project_params)?,
|
||||||
|
|
||||||
let vfs = Vfs::new(template);
|
|
||||||
vfs.set_watch_enabled(false);
|
|
||||||
|
|
||||||
let mut queue = VecDeque::with_capacity(8);
|
|
||||||
for entry in vfs.read_dir("")? {
|
|
||||||
queue.push_back(entry?.path().to_path_buf())
|
|
||||||
}
|
|
||||||
|
|
||||||
while let Some(mut path) = queue.pop_front() {
|
|
||||||
let metadata = vfs.metadata(&path)?;
|
|
||||||
if metadata.is_dir() {
|
|
||||||
fs_err::create_dir(base_path.join(&path))?;
|
|
||||||
for entry in vfs.read_dir(&path)? {
|
|
||||||
queue.push_back(entry?.path().to_path_buf());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let content = vfs.read_to_string_lf_normalized(&path)?;
|
|
||||||
if let Some(file_stem) = path.file_name().and_then(OsStr::to_str) {
|
|
||||||
if file_stem == GIT_IGNORE_PLACEHOLDER && !self.skip_git {
|
|
||||||
path.set_file_name(".gitignore");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write_if_not_exists(
|
|
||||||
&base_path.join(&path),
|
|
||||||
&project_params.render_template(&content),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.skip_git && should_git_init(&base_path) {
|
|
||||||
log::debug!("Initializing Git repository...");
|
|
||||||
|
|
||||||
let status = Command::new("git")
|
|
||||||
.arg("init")
|
|
||||||
.current_dir(&base_path)
|
|
||||||
.status()?;
|
|
||||||
|
|
||||||
if !status.success() {
|
|
||||||
bail!("git init failed: status code {:?}", status.code());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Created project successfully.");
|
println!("Created project successfully.");
|
||||||
@@ -123,32 +78,6 @@ pub enum InitKind {
|
|||||||
Plugin,
|
Plugin,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InitKind {
|
|
||||||
fn template(&self) -> InMemoryFs {
|
|
||||||
let template_path = match self {
|
|
||||||
Self::Place => "place",
|
|
||||||
Self::Model => "model",
|
|
||||||
Self::Plugin => "plugin",
|
|
||||||
};
|
|
||||||
|
|
||||||
let snapshot: VfsSnapshot = bincode::deserialize(TEMPLATE_BINCODE)
|
|
||||||
.expect("Rojo's templates were not properly packed into Rojo's binary");
|
|
||||||
|
|
||||||
if let VfsSnapshot::Dir { mut children } = snapshot {
|
|
||||||
if let Some(template) = children.remove(template_path) {
|
|
||||||
let mut fs = InMemoryFs::new();
|
|
||||||
fs.load_snapshot("", template)
|
|
||||||
.expect("loading a template in memory should never fail");
|
|
||||||
fs
|
|
||||||
} else {
|
|
||||||
panic!("template for project type {:?} is missing", self)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panic!("Rojo's templates were packed as a file instead of a directory")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for InitKind {
|
impl FromStr for InitKind {
|
||||||
type Err = anyhow::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
@@ -165,6 +94,92 @@ impl FromStr for InitKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn init_place(base_path: &Path, project_params: ProjectParams) -> anyhow::Result<()> {
|
||||||
|
println!("Creating new place project '{}'", project_params.name);
|
||||||
|
|
||||||
|
let project_file = project_params.render_template(PLACE_PROJECT);
|
||||||
|
try_create_project(base_path, &project_file)?;
|
||||||
|
|
||||||
|
let readme = project_params.render_template(PLACE_README);
|
||||||
|
write_if_not_exists(&base_path.join("README.md"), &readme)?;
|
||||||
|
|
||||||
|
let src = base_path.join("src");
|
||||||
|
fs::create_dir_all(&src)?;
|
||||||
|
|
||||||
|
let src_shared = src.join("shared");
|
||||||
|
fs::create_dir_all(src.join(&src_shared))?;
|
||||||
|
|
||||||
|
let src_server = src.join("server");
|
||||||
|
fs::create_dir_all(src.join(&src_server))?;
|
||||||
|
|
||||||
|
let src_client = src.join("client");
|
||||||
|
fs::create_dir_all(src.join(&src_client))?;
|
||||||
|
|
||||||
|
write_if_not_exists(
|
||||||
|
&src_shared.join("Hello.luau"),
|
||||||
|
"return function()\n\tprint(\"Hello, world!\")\nend",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
write_if_not_exists(
|
||||||
|
&src_server.join("init.server.luau"),
|
||||||
|
"print(\"Hello world, from server!\")",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
write_if_not_exists(
|
||||||
|
&src_client.join("init.client.luau"),
|
||||||
|
"print(\"Hello world, from client!\")",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let git_ignore = project_params.render_template(PLACE_GIT_IGNORE);
|
||||||
|
try_git_init(base_path, &git_ignore)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_model(base_path: &Path, project_params: ProjectParams) -> anyhow::Result<()> {
|
||||||
|
println!("Creating new model project '{}'", project_params.name);
|
||||||
|
|
||||||
|
let project_file = project_params.render_template(MODEL_PROJECT);
|
||||||
|
try_create_project(base_path, &project_file)?;
|
||||||
|
|
||||||
|
let readme = project_params.render_template(MODEL_README);
|
||||||
|
write_if_not_exists(&base_path.join("README.md"), &readme)?;
|
||||||
|
|
||||||
|
let src = base_path.join("src");
|
||||||
|
fs::create_dir_all(&src)?;
|
||||||
|
|
||||||
|
let init = project_params.render_template(MODEL_INIT);
|
||||||
|
write_if_not_exists(&src.join("init.luau"), &init)?;
|
||||||
|
|
||||||
|
let git_ignore = project_params.render_template(MODEL_GIT_IGNORE);
|
||||||
|
try_git_init(base_path, &git_ignore)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_plugin(base_path: &Path, project_params: ProjectParams) -> anyhow::Result<()> {
|
||||||
|
println!("Creating new plugin project '{}'", project_params.name);
|
||||||
|
|
||||||
|
let project_file = project_params.render_template(PLUGIN_PROJECT);
|
||||||
|
try_create_project(base_path, &project_file)?;
|
||||||
|
|
||||||
|
let readme = project_params.render_template(PLUGIN_README);
|
||||||
|
write_if_not_exists(&base_path.join("README.md"), &readme)?;
|
||||||
|
|
||||||
|
let src = base_path.join("src");
|
||||||
|
fs::create_dir_all(&src)?;
|
||||||
|
|
||||||
|
write_if_not_exists(
|
||||||
|
&src.join("init.server.luau"),
|
||||||
|
"print(\"Hello world, from plugin!\")\n",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let git_ignore = project_params.render_template(PLUGIN_GIT_IGNORE);
|
||||||
|
try_git_init(base_path, &git_ignore)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Contains parameters used in templates to create a project.
|
/// Contains parameters used in templates to create a project.
|
||||||
struct ProjectParams {
|
struct ProjectParams {
|
||||||
name: String,
|
name: String,
|
||||||
@@ -179,6 +194,23 @@ impl ProjectParams {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to initialize a Git repository if necessary, and create .gitignore.
|
||||||
|
fn try_git_init(path: &Path, git_ignore: &str) -> Result<(), anyhow::Error> {
|
||||||
|
if should_git_init(path) {
|
||||||
|
log::debug!("Initializing Git repository...");
|
||||||
|
|
||||||
|
let status = Command::new("git").arg("init").current_dir(path).status()?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
bail!("git init failed: status code {:?}", status.code());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write_if_not_exists(&path.join(".gitignore"), git_ignore)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Tells whether we should initialize a Git repository inside the given path.
|
/// Tells whether we should initialize a Git repository inside the given path.
|
||||||
///
|
///
|
||||||
/// Will return false if the user doesn't have Git installed or if the path is
|
/// Will return false if the user doesn't have Git installed or if the path is
|
||||||
@@ -219,3 +251,29 @@ fn write_if_not_exists(path: &Path, contents: &str) -> Result<(), anyhow::Error>
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to create a project file and fail if it already exists.
|
||||||
|
fn try_create_project(base_path: &Path, contents: &str) -> Result<(), anyhow::Error> {
|
||||||
|
let project_path = base_path.join("default.project.json");
|
||||||
|
|
||||||
|
let file_res = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.open(&project_path);
|
||||||
|
|
||||||
|
let mut file = match file_res {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(err) => {
|
||||||
|
return match err.kind() {
|
||||||
|
io::ErrorKind::AlreadyExists => {
|
||||||
|
bail!("Project file already exists: {}", project_path.display())
|
||||||
|
}
|
||||||
|
_ => Err(err.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
file.write_all(contents.as_bytes())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::{bail, Context};
|
use anyhow::{bail, format_err, Context};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use memofs::Vfs;
|
use memofs::Vfs;
|
||||||
use reqwest::{
|
use reqwest::{
|
||||||
@@ -90,6 +91,32 @@ impl UploadCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The kind of asset to upload to the website. Affects what endpoints Rojo uses
|
||||||
|
/// and changes how the asset is built.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum UploadKind {
|
||||||
|
/// Upload to a place.
|
||||||
|
Place,
|
||||||
|
|
||||||
|
/// Upload to a model-like asset, like a Model, Plugin, or Package.
|
||||||
|
Model,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for UploadKind {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(source: &str) -> Result<Self, Self::Err> {
|
||||||
|
match source {
|
||||||
|
"place" => Ok(UploadKind::Place),
|
||||||
|
"model" => Ok(UploadKind::Model),
|
||||||
|
attempted => Err(format_err!(
|
||||||
|
"Invalid upload kind '{}'. Valid kinds are: place, model",
|
||||||
|
attempted
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn do_upload(buffer: Vec<u8>, asset_id: u64, cookie: &str) -> anyhow::Result<()> {
|
fn do_upload(buffer: Vec<u8>, asset_id: u64, cookie: &str) -> anyhow::Result<()> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"https://data.roblox.com/Data/Upload.ashx?assetid={}",
|
"https://data.roblox.com/Data/Upload.ashx?assetid={}",
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ impl AmbiguousValue {
|
|||||||
|
|
||||||
match &property.data_type {
|
match &property.data_type {
|
||||||
DataType::Enum(enum_name) => {
|
DataType::Enum(enum_name) => {
|
||||||
let database = rbx_reflection_database::get().unwrap();
|
let database = rbx_reflection_database::get();
|
||||||
|
|
||||||
let enum_descriptor = database.enums.get(enum_name).ok_or_else(|| {
|
let enum_descriptor = database.enums.get(enum_name).ok_or_else(|| {
|
||||||
format_err!("Unknown enum {}. This is a Rojo bug!", enum_name)
|
format_err!("Unknown enum {}. This is a Rojo bug!", enum_name)
|
||||||
@@ -203,7 +203,7 @@ fn find_descriptor(
|
|||||||
class_name: &str,
|
class_name: &str,
|
||||||
prop_name: &str,
|
prop_name: &str,
|
||||||
) -> Option<&'static PropertyDescriptor<'static>> {
|
) -> Option<&'static PropertyDescriptor<'static>> {
|
||||||
let database = rbx_reflection_database::get().unwrap();
|
let database = rbx_reflection_database::get();
|
||||||
let mut current_class_name = class_name;
|
let mut current_class_name = class_name;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ pub enum InstigatingSource {
|
|||||||
ProjectNode(
|
ProjectNode(
|
||||||
#[serde(serialize_with = "path_serializer::serialize_absolute")] PathBuf,
|
#[serde(serialize_with = "path_serializer::serialize_absolute")] PathBuf,
|
||||||
String,
|
String,
|
||||||
Box<ProjectNode>,
|
ProjectNode,
|
||||||
Option<String>,
|
Option<String>,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ impl RojoTree {
|
|||||||
self.inner.root_ref()
|
self.inner.root_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_instance(&self, id: Ref) -> Option<InstanceWithMeta<'_>> {
|
pub fn get_instance(&self, id: Ref) -> Option<InstanceWithMeta> {
|
||||||
if let Some(instance) = self.inner.get_by_ref(id) {
|
if let Some(instance) = self.inner.get_by_ref(id) {
|
||||||
let metadata = self.metadata_map.get(&id).unwrap();
|
let metadata = self.metadata_map.get(&id).unwrap();
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ impl RojoTree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_instance_mut(&mut self, id: Ref) -> Option<InstanceWithMetaMut<'_>> {
|
pub fn get_instance_mut(&mut self, id: Ref) -> Option<InstanceWithMetaMut> {
|
||||||
if let Some(instance) = self.inner.get_by_ref_mut(id) {
|
if let Some(instance) = self.inner.get_by_ref_mut(id) {
|
||||||
let metadata = self.metadata_map.get_mut(&id).unwrap();
|
let metadata = self.metadata_map.get_mut(&id).unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ pub fn snapshot_lua(
|
|||||||
script_type: ScriptType,
|
script_type: ScriptType,
|
||||||
) -> anyhow::Result<Option<InstanceSnapshot>> {
|
) -> anyhow::Result<Option<InstanceSnapshot>> {
|
||||||
let run_context_enums = &rbx_reflection_database::get()
|
let run_context_enums = &rbx_reflection_database::get()
|
||||||
.unwrap()
|
|
||||||
.enums
|
.enums
|
||||||
.get("RunContext")
|
.get("RunContext")
|
||||||
.expect("Unable to get RunContext enums!")
|
.expect("Unable to get RunContext enums!")
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ pub fn snapshot_project_node(
|
|||||||
metadata.instigating_source = Some(InstigatingSource::ProjectNode(
|
metadata.instigating_source = Some(InstigatingSource::ProjectNode(
|
||||||
project_path.to_path_buf(),
|
project_path.to_path_buf(),
|
||||||
instance_name.to_string(),
|
instance_name.to_string(),
|
||||||
Box::new(node.clone()),
|
node.clone(),
|
||||||
parent_class.map(|name| name.to_owned()),
|
parent_class.map(|name| name.to_owned()),
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -313,7 +313,7 @@ fn infer_class_name(name: &str, parent_class: Option<&str>) -> Option<Ustr> {
|
|||||||
// Members of DataModel with names that match known services are
|
// Members of DataModel with names that match known services are
|
||||||
// probably supposed to be those services.
|
// probably supposed to be those services.
|
||||||
|
|
||||||
let descriptor = rbx_reflection_database::get().unwrap().classes.get(name)?;
|
let descriptor = rbx_reflection_database::get().classes.get(name)?;
|
||||||
|
|
||||||
if descriptor.tags.contains(&ClassTag::Service) {
|
if descriptor.tags.contains(&ClassTag::Service) {
|
||||||
return Some(ustr(name));
|
return Some(ustr(name));
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ impl TestServeSession {
|
|||||||
Ok(serde_json::from_str(&body).expect("Server returned malformed response"))
|
Ok(serde_json::from_str(&body).expect("Server returned malformed response"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_api_read(&self, id: Ref) -> Result<ReadResponse<'_>, reqwest::Error> {
|
pub fn get_api_read(&self, id: Ref) -> Result<ReadResponse, reqwest::Error> {
|
||||||
let url = format!("http://localhost:{}/api/read/{}", self.port, id);
|
let url = format!("http://localhost:{}/api/read/{}", self.port, id);
|
||||||
let body = reqwest::blocking::get(url)?.text()?;
|
let body = reqwest::blocking::get(url)?.text()?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user