mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-23 05:51:39 +00:00
Update dependencies and refactor git integration
- Added new dependencies in `Cargo.lock` and updated `Cargo.toml` to include `gix` for improved git functionality. - Refactored git-related functions to utilize the `gix` library, enhancing performance and reliability in handling git operations. - Simplified the process of initializing git repositories and committing changes within the CLI commands. - Updated tests to reflect changes in git integration and ensure proper functionality. These changes aim to modernize the git handling in the project and improve overall code quality.
This commit is contained in:
parent
d8d6a53fce
commit
0a359ccaf1
12 changed files with 1954 additions and 473 deletions
1188
Cargo.lock
generated
1188
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -107,6 +107,7 @@ data-encoding = "2.10.0"
|
||||||
pathdiff = "0.2.3"
|
pathdiff = "0.2.3"
|
||||||
|
|
||||||
blake3 = "1.8.3"
|
blake3 = "1.8.3"
|
||||||
|
gix = { version = "0.80", default-features = true, features = ["blocking-network-client"] }
|
||||||
sha1 = "0.10"
|
sha1 = "0.10"
|
||||||
indexmap = { version = "2.13.0", features = ["serde"] }
|
indexmap = { version = "2.13.0", features = ["serde"] }
|
||||||
rmp-serde = "1.3.1"
|
rmp-serde = "1.3.1"
|
||||||
|
|
|
||||||
35
TODO.txt
35
TODO.txt
|
|
@ -5,15 +5,6 @@ get the schemas for everything into cursor somehow
|
||||||
setup nightly build
|
setup nightly build
|
||||||
suffix version for beta
|
suffix version for beta
|
||||||
|
|
||||||
[mcp]
|
|
||||||
* atlas_sync
|
|
||||||
- Basically equiv to git fast-forward merge
|
|
||||||
- Invokes Atlas sync from Roblox
|
|
||||||
- If there are NO unselected items in the tree (e.g. all were automatically selected), it just automatically hits accept
|
|
||||||
- Does nothing if we are running in connected-mode (already syncing)
|
|
||||||
- Agent should await the completion of this sync before proceeding
|
|
||||||
- If we are in connected-mode we will need to figure out a way to await syncing to finish to ensure state is good
|
|
||||||
|
|
||||||
[merging]
|
[merging]
|
||||||
we should make the property diffs have arrows next to eachother so you can like "undo" certain things
|
we should make the property diffs have arrows next to eachother so you can like "undo" certain things
|
||||||
need proper merging support in this UI.
|
need proper merging support in this UI.
|
||||||
|
|
@ -336,10 +327,24 @@ because have already loaded it, we can detect this.
|
||||||
this is an optimization to avoid writing 100,000 files or something on larger projects when file bytes match perfectly, or directories verifiably already exist.
|
this is an optimization to avoid writing 100,000 files or something on larger projects when file bytes match perfectly, or directories verifiably already exist.
|
||||||
make sure this is applied to plugin-based syncback, too.
|
make sure this is applied to plugin-based syncback, too.
|
||||||
|
|
||||||
[git]
|
[mcp]
|
||||||
I notice we actually use the git cli in a lot of places. is there a git library we can use instead?
|
* atlas_sync
|
||||||
|
- Basically equiv to git fast-forward merge
|
||||||
|
- Atlas plugin automatically connects (tries every 5s if nothing there, do not spam errors) via https://create.roblox.com/docs/reference/engine/classes/HttpService#CreateWebStreamClient
|
||||||
|
- Atlas serve automatically hosts a http based mcp server (NOT stdio based!)
|
||||||
|
- Decide whether it's easier to make a separate server from the api.rs file, or integrate it - integrating would be cleaner, but im not sure if it would support without a massive amount of changes
|
||||||
|
- When an agent invokes atlas_sync, it invokes Atlas sync from Roblox in the plugin as if the user clicked the sync button
|
||||||
|
- Must be on a mutex, so multiple agents cannot invoke it at the same time. Just refuses if it's currently happening
|
||||||
|
- Also it needs to refuse it if we're actively connected on the regular api layer too, not just the mcp based command.
|
||||||
|
- If we are connected already, respond to the agent with a proper notice. Make sure if we are in connected-mode where all things are syncing automatically all the time, we tell the agent. We need to tell the agent the configuration of the lua plugin probably?
|
||||||
|
- If we are in connected-mode we will need to figure out a way to await syncing to finish to ensure state is good?
|
||||||
|
- Upon the plugin connecting, if there are NO changes (Always/Initial) it should just immediately accept
|
||||||
|
- If all changes are pre-selected, e.g. from our git pre selection system, then we "fast-forward" by clicking accept immediately.
|
||||||
|
- If there are actually changes, the agent should hang while waiting for the user to either accept/deny them
|
||||||
|
- The output of the mcp request needs to conclude that it was a success and what changes were synced AND if it was rejected + what changes were presented to the user to sync.
|
||||||
|
- Let's just give a list of file paths on the actual disk (relative to project so like ReplicatedStorage/SomeFile.luau or ReplicatedStorage/SomeModel.json5 etc) ?
|
||||||
|
- Needs to include the DIRECTION, too.
|
||||||
|
- I'm not sure what is the most value on this, but we absolutely need to give the agent a response on what was actually synced, ideally what the content was(?), and the directions of sync.
|
||||||
|
- Does nothing if we are running in connected-mode (already syncing)
|
||||||
|
- Agent should await the completion of this sync before proceeding, this mcp command needs to yield and give the response as described above.
|
||||||
|
|
||||||
[cli]
|
|
||||||
make console tabbing support work
|
|
||||||
when i say console tabbing, like i can type "git c" and it will recommend "git checkout"
|
|
||||||
is there a rust crate to accomplish this? can we enable it on our current argument parser library being used?
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ expression: normalized_stdout
|
||||||
Writing default.project.json5
|
Writing default.project.json5
|
||||||
Writing src/adjacent.luau
|
Writing src/adjacent.luau
|
||||||
Writing src/adjacent.meta.json5
|
Writing src/adjacent.meta.json5
|
||||||
Writing src/init/init.meta.json5
|
Writing src/init_/init.meta.json5
|
||||||
Writing src/model.model.json5
|
Writing src/model.model.json5
|
||||||
Writing src/init
|
Writing src/init_
|
||||||
|
Removing src/init
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
source: tests/tests/syncback.rs
|
||||||
|
expression: src/init_/init.meta.json5
|
||||||
|
---
|
||||||
|
{
|
||||||
|
attributes: {
|
||||||
|
trigger_reserialization: true
|
||||||
|
},
|
||||||
|
name: "init"
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Command, Stdio};
|
|
||||||
|
|
||||||
use anyhow::{bail, Context};
|
use anyhow::{bail, Context};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
@ -95,25 +94,8 @@ impl CloneCommand {
|
||||||
|
|
||||||
syncback.run(global)?;
|
syncback.run(global)?;
|
||||||
|
|
||||||
// Commit syncback result
|
|
||||||
if !skip_git {
|
if !skip_git {
|
||||||
log::info!("Staging syncback changes...");
|
crate::git::git_add_all_and_commit(&path, "syncback");
|
||||||
let _ = Command::new("git")
|
|
||||||
.args(["add", "."])
|
|
||||||
.current_dir(&path)
|
|
||||||
.stdin(Stdio::null())
|
|
||||||
.stdout(Stdio::null())
|
|
||||||
.stderr(Stdio::null())
|
|
||||||
.status();
|
|
||||||
|
|
||||||
log::info!("Committing syncback changes...");
|
|
||||||
let _ = Command::new("git")
|
|
||||||
.args(["commit", "--no-verify", "-m", "syncback"])
|
|
||||||
.current_dir(&path)
|
|
||||||
.stdin(Stdio::null())
|
|
||||||
.stdout(Stdio::null())
|
|
||||||
.stderr(Stdio::null())
|
|
||||||
.status();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.skip_cd {
|
if !self.skip_cd {
|
||||||
|
|
|
||||||
117
src/cli/init.rs
117
src/cli/init.rs
|
|
@ -1,5 +1,6 @@
|
||||||
use std::process::{Command, Stdio};
|
use std::num::NonZero;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
|
@ -24,7 +25,7 @@ static TEMPLATE_BINCODE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/templ
|
||||||
/// Initializes a new Rojo project.
|
/// Initializes a new Rojo project.
|
||||||
///
|
///
|
||||||
/// By default, this will attempt to initialize a 'git' repository in the
|
/// By default, this will attempt to initialize a 'git' repository in the
|
||||||
/// project directory if `git` is installed. To avoid this, pass `--skip-git`.
|
/// project directory. 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.
|
||||||
|
|
@ -57,7 +58,6 @@ impl InitCommand {
|
||||||
|
|
||||||
let base_path = resolve_path(&self.path);
|
let base_path = resolve_path(&self.path);
|
||||||
|
|
||||||
// Check if directory exists and is non-empty
|
|
||||||
if base_path.exists() {
|
if base_path.exists() {
|
||||||
let is_empty = base_path.read_dir()?.next().is_none();
|
let is_empty = base_path.read_dir()?.next().is_none();
|
||||||
if !is_empty {
|
if !is_empty {
|
||||||
|
|
@ -122,31 +122,14 @@ impl InitCommand {
|
||||||
let did_git_init = if !self.skip_git && should_git_init(&base_path) {
|
let did_git_init = if !self.skip_git && should_git_init(&base_path) {
|
||||||
log::debug!("Initializing Git repository...");
|
log::debug!("Initializing Git repository...");
|
||||||
|
|
||||||
let status = Command::new("git")
|
let mut repo = gix::init(&base_path).context("Failed to initialize git repository")?;
|
||||||
.arg("init")
|
|
||||||
.current_dir(&base_path)
|
|
||||||
.status()?;
|
|
||||||
|
|
||||||
if !status.success() {
|
{
|
||||||
bail!("git init failed: status code {:?}", status.code());
|
let mut config = repo.config_snapshot_mut();
|
||||||
}
|
let _ = config.set_raw_value(&gix::config::tree::Core::AUTO_CRLF, "false");
|
||||||
|
let _ = config.set_raw_value(&gix::config::tree::Core::EOL, "lf");
|
||||||
for (key, value) in [
|
let _ = config.set_raw_value(&gix::config::tree::Core::SAFE_CRLF, "false");
|
||||||
("core.autocrlf", "false"),
|
let _ = config.commit_auto_rollback();
|
||||||
("core.eol", "lf"),
|
|
||||||
("core.safecrlf", "false"),
|
|
||||||
// ("core.splitIndex", "true"),
|
|
||||||
// ("core.fsmonitor", "true"),
|
|
||||||
// ("core.untrackedcache", "true"),
|
|
||||||
// ("feature.manyFiles", "true"),
|
|
||||||
] {
|
|
||||||
let _ = Command::new("git")
|
|
||||||
.args(["config", "--local", key, value])
|
|
||||||
.current_dir(&base_path)
|
|
||||||
.stdin(Stdio::null())
|
|
||||||
.stdout(Stdio::null())
|
|
||||||
.stderr(Stdio::null())
|
|
||||||
.status();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
@ -158,54 +141,37 @@ impl InitCommand {
|
||||||
log::debug!("Cloning cursor rules...");
|
log::debug!("Cloning cursor rules...");
|
||||||
|
|
||||||
let cursor_dir = base_path.join(".cursor");
|
let cursor_dir = base_path.join(".cursor");
|
||||||
let result = Command::new("git")
|
let result = (|| -> anyhow::Result<()> {
|
||||||
.args([
|
let prep = gix::prepare_clone(
|
||||||
"clone",
|
|
||||||
"--depth",
|
|
||||||
"1",
|
|
||||||
"https://github.com/jrmelsha/cursor-rules.git",
|
"https://github.com/jrmelsha/cursor-rules.git",
|
||||||
".cursor",
|
&cursor_dir,
|
||||||
])
|
)?;
|
||||||
.current_dir(&base_path)
|
let mut prep = prep.with_shallow(gix::remote::fetch::Shallow::DepthAtRemote(
|
||||||
.stdin(Stdio::null())
|
NonZero::new(1).unwrap(),
|
||||||
.stdout(Stdio::null())
|
));
|
||||||
.stderr(Stdio::null())
|
let (mut checkout, _) =
|
||||||
.env("GIT_TERMINAL_PROMPT", "0")
|
prep.fetch_then_checkout(gix::progress::Discard, &AtomicBool::new(false))?;
|
||||||
.status();
|
let (_repo, _) =
|
||||||
|
checkout.main_worktree(gix::progress::Discard, &AtomicBool::new(false))?;
|
||||||
|
Ok(())
|
||||||
|
})();
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(status) if status.success() => {
|
Ok(()) => {
|
||||||
// Remove .git so it's just files, not a submodule
|
|
||||||
let git_dir = cursor_dir.join(".git");
|
let git_dir = cursor_dir.join(".git");
|
||||||
if git_dir.exists() {
|
if git_dir.exists() {
|
||||||
let _ = fs::remove_dir_all(&git_dir);
|
let _ = fs::remove_dir_all(&git_dir);
|
||||||
}
|
}
|
||||||
println!("Cloned cursor rules successfully.");
|
println!("Cloned cursor rules successfully.");
|
||||||
}
|
}
|
||||||
_ => {
|
Err(_) => {
|
||||||
log::debug!("Failed to clone cursor rules, skipping.");
|
log::debug!("Failed to clone cursor rules, skipping.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if did_git_init {
|
if did_git_init {
|
||||||
log::info!("Staging initial project...");
|
crate::git::git_add_all_and_commit(&base_path, "Initial commit");
|
||||||
let _ = Command::new("git")
|
|
||||||
.args(["add", "."])
|
|
||||||
.current_dir(&base_path)
|
|
||||||
.stdin(Stdio::null())
|
|
||||||
.stdout(Stdio::null())
|
|
||||||
.stderr(Stdio::null())
|
|
||||||
.status();
|
|
||||||
|
|
||||||
log::info!("Committing initial project...");
|
|
||||||
let _ = Command::new("git")
|
|
||||||
.args(["commit", "--no-verify", "-m", "Initial commit"])
|
|
||||||
.current_dir(&base_path)
|
|
||||||
.stdin(Stdio::null())
|
|
||||||
.stdout(Stdio::null())
|
|
||||||
.stderr(Stdio::null())
|
|
||||||
.status();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Created project successfully.");
|
println!("Created project successfully.");
|
||||||
|
|
@ -219,6 +185,13 @@ impl InitCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tells whether we should initialize a Git repository inside the given path.
|
||||||
|
///
|
||||||
|
/// Returns true if the path is not already inside a Git repository.
|
||||||
|
fn should_git_init(path: &Path) -> bool {
|
||||||
|
gix::discover(path).is_err()
|
||||||
|
}
|
||||||
|
|
||||||
/// The templates we support for initializing a Rojo project.
|
/// The templates we support for initializing a Rojo project.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum InitKind {
|
pub enum InitKind {
|
||||||
|
|
@ -275,14 +248,12 @@ impl FromStr for InitKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains parameters used in templates to create a project.
|
|
||||||
struct ProjectParams {
|
struct ProjectParams {
|
||||||
name: String,
|
name: String,
|
||||||
place_id: Option<u64>,
|
place_id: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProjectParams {
|
impl ProjectParams {
|
||||||
/// Render a template by replacing variables with project parameters.
|
|
||||||
fn render_template(&self, template: &str) -> String {
|
fn render_template(&self, template: &str) -> String {
|
||||||
let place_id_str = self
|
let place_id_str = self
|
||||||
.place_id
|
.place_id
|
||||||
|
|
@ -296,28 +267,6 @@ impl ProjectParams {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
|
||||||
/// already inside a Git repository.
|
|
||||||
fn should_git_init(path: &Path) -> bool {
|
|
||||||
let result = Command::new("git")
|
|
||||||
.args(["rev-parse", "--is-inside-work-tree"])
|
|
||||||
.stdout(Stdio::null())
|
|
||||||
.stderr(Stdio::null())
|
|
||||||
.current_dir(path)
|
|
||||||
.status();
|
|
||||||
|
|
||||||
match result {
|
|
||||||
// If the command ran, but returned a non-zero exit code, we are not in
|
|
||||||
// a Git repo and we should initialize one.
|
|
||||||
Ok(status) => !status.success(),
|
|
||||||
|
|
||||||
// If the command failed to run, we probably don't have Git installed.
|
|
||||||
Err(_) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a file if it does not exist yet, otherwise, leave it alone.
|
/// Write a file if it does not exist yet, otherwise, leave it alone.
|
||||||
fn write_if_not_exists(path: &Path, contents: &str) -> Result<(), anyhow::Error> {
|
fn write_if_not_exists(path: &Path, contents: &str) -> Result<(), anyhow::Error> {
|
||||||
let file_res = OpenOptions::new().write(true).create_new(true).open(path);
|
let file_res = OpenOptions::new().write(true).create_new(true).open(path);
|
||||||
|
|
|
||||||
994
src/git.rs
994
src/git.rs
File diff suppressed because it is too large
Load diff
|
|
@ -28,6 +28,7 @@ mod web;
|
||||||
|
|
||||||
// TODO: Work out what we should expose publicly
|
// TODO: Work out what we should expose publicly
|
||||||
|
|
||||||
|
pub use git::{git_add, git_add_all_and_commit};
|
||||||
pub use project::*;
|
pub use project::*;
|
||||||
pub use rojo_ref::*;
|
pub use rojo_ref::*;
|
||||||
pub use serve_session::TreeFreshnessReport;
|
pub use serve_session::TreeFreshnessReport;
|
||||||
|
|
|
||||||
|
|
@ -203,21 +203,17 @@ impl TestServeSession {
|
||||||
.expect("Couldn't copy project to temporary directory");
|
.expect("Couldn't copy project to temporary directory");
|
||||||
|
|
||||||
// Initialize git repo and run setup callback
|
// Initialize git repo and run setup callback
|
||||||
Command::new("git")
|
let mut repo = gix::init(&project_path).expect("gix init failed");
|
||||||
.args(["init"])
|
{
|
||||||
.current_dir(&project_path)
|
let mut config = repo.config_snapshot_mut();
|
||||||
.output()
|
config
|
||||||
.expect("git init failed");
|
.set_raw_value(&gix::config::tree::User::NAME, "Test")
|
||||||
Command::new("git")
|
|
||||||
.args(["config", "user.email", "test@test.com"])
|
|
||||||
.current_dir(&project_path)
|
|
||||||
.output()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Command::new("git")
|
config
|
||||||
.args(["config", "user.name", "Test"])
|
.set_raw_value(&gix::config::tree::User::EMAIL, "test@test.com")
|
||||||
.current_dir(&project_path)
|
|
||||||
.output()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let _ = config.commit_auto_rollback().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
setup(&project_path);
|
setup(&project_path);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
//! - Non-script changes are in changedIds but not in scriptCommittedHashes
|
//! - Non-script changes are in changedIds but not in scriptCommittedHashes
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{fs, thread};
|
use std::{fs, thread};
|
||||||
|
|
@ -24,27 +24,15 @@ use rbx_dom_weak::{ustr, UstrMap};
|
||||||
use crate::rojo_test::serve_util::TestServeSession;
|
use crate::rojo_test::serve_util::TestServeSession;
|
||||||
|
|
||||||
fn git_commit_all(dir: &Path, msg: &str) {
|
fn git_commit_all(dir: &Path, msg: &str) {
|
||||||
Command::new("git")
|
librojo::git::git_add_all_and_commit(dir, msg);
|
||||||
.args(["add", "-A"])
|
|
||||||
.current_dir(dir)
|
|
||||||
.output()
|
|
||||||
.expect("git add failed");
|
|
||||||
Command::new("git")
|
|
||||||
.args(["commit", "-m", msg])
|
|
||||||
.current_dir(dir)
|
|
||||||
.output()
|
|
||||||
.expect("git commit failed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn git_stage(dir: &Path, file: &str) {
|
fn git_stage(dir: &Path, file: &str) {
|
||||||
Command::new("git")
|
librojo::git_add(dir, &[PathBuf::from(file)]);
|
||||||
.args(["add", file])
|
|
||||||
.current_dir(dir)
|
|
||||||
.output()
|
|
||||||
.expect("git stage failed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn git_is_staged(dir: &Path, file: &str) -> bool {
|
fn git_is_staged(dir: &Path, file: &str) -> bool {
|
||||||
|
use std::process::Command;
|
||||||
let output = Command::new("git")
|
let output = Command::new("git")
|
||||||
.args(["diff", "--cached", "--name-only"])
|
.args(["diff", "--cached", "--name-only"])
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ syncback_tests! {
|
||||||
// do not leave any trace of themselves (new files get .json5)
|
// do not leave any trace of themselves (new files get .json5)
|
||||||
ref_properties_pruned => ["src/Pointer1.model.json5", "src/Pointer2.model.json5", "src/Pointer3.model.json5"],
|
ref_properties_pruned => ["src/Pointer1.model.json5", "src/Pointer2.model.json5", "src/Pointer3.model.json5"],
|
||||||
// Ensures that the `$schema` field roundtrips with syncback
|
// Ensures that the `$schema` field roundtrips with syncback
|
||||||
schema_roundtrip => ["default.project.json5", "src/model.model.json5", "src/init/init.meta.json5", "src/adjacent.meta.json5"],
|
schema_roundtrip => ["default.project.json5", "src/model.model.json5", "src/init_/init.meta.json5", "src/adjacent.meta.json5"],
|
||||||
// Ensures that StringValues inside project files are written to the
|
// Ensures that StringValues inside project files are written to the
|
||||||
// project file, but only if they don't have `$path` set
|
// project file, but only if they don't have `$path` set
|
||||||
string_value_project => ["default.project.json5"],
|
string_value_project => ["default.project.json5"],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue