mirror of
https://github.com/shiroyashik/sculptor.git
synced 2025-12-06 04:51:13 +03:00
Small changes
This commit is contained in:
parent
7196c719e2
commit
0526cc9412
8 changed files with 84 additions and 69 deletions
42
Cargo.lock
generated
42
Cargo.lock
generated
|
|
@ -109,14 +109,14 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.7.6"
|
version = "0.7.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec"
|
checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum-core",
|
"axum-core",
|
||||||
"axum-macros",
|
"axum-macros",
|
||||||
"base64 0.21.7",
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
|
|
@ -147,9 +147,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-core"
|
name = "axum-core"
|
||||||
version = "0.4.4"
|
version = "0.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00"
|
checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|
@ -192,12 +192,6 @@ dependencies = [
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "base64"
|
|
||||||
version = "0.21.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.22.1"
|
version = "0.22.1"
|
||||||
|
|
@ -1553,7 +1547,7 @@ version = "0.12.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
|
checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
|
|
@ -1653,7 +1647,7 @@ version = "2.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
|
checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -1716,7 +1710,7 @@ version = "0.4.0-dev"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
"base64 0.22.1",
|
"base64",
|
||||||
"chrono",
|
"chrono",
|
||||||
"cross",
|
"cross",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
|
|
@ -2038,18 +2032,18 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.63"
|
version = "1.0.64"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.63"
|
version = "1.0.64"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -2164,9 +2158,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-tungstenite"
|
name = "tokio-tungstenite"
|
||||||
version = "0.23.1"
|
version = "0.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd"
|
checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"log",
|
"log",
|
||||||
|
|
@ -2263,9 +2257,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-http"
|
name = "tower-http"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41515cc9e193536d93fd0dbbea0c73819c08eca76e0b30909a325c3ec90985bb"
|
checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|
@ -2382,9 +2376,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tungstenite"
|
name = "tungstenite"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8"
|
checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ version = "0.4.0-dev"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Logging
|
# Logging
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter", "chrono"] }
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter", "chrono"] }
|
||||||
|
|
@ -14,7 +16,7 @@ tracing = "0.1.40"
|
||||||
|
|
||||||
# Errors handelers
|
# Errors handelers
|
||||||
anyhow = "1.0.83"
|
anyhow = "1.0.83"
|
||||||
thiserror = "1.0.63"
|
thiserror = "1.0.64"
|
||||||
chrono = { version = "0.4.38", features = ["now", "serde"] }
|
chrono = { version = "0.4.38", features = ["now", "serde"] }
|
||||||
serde = { version = "1.0.201", features = ["derive"] }
|
serde = { version = "1.0.201", features = ["derive"] }
|
||||||
serde_json = "1.0.117"
|
serde_json = "1.0.117"
|
||||||
|
|
@ -35,8 +37,8 @@ ring = "0.17.8"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
|
||||||
# Web framework
|
# Web framework
|
||||||
axum = { version = "0.7.6", features = ["ws", "macros", "http2"] }
|
axum = { version = "0.7.7", features = ["ws", "macros", "http2"] }
|
||||||
tower-http = { version = "0.6.0", features = ["trace"] }
|
tower-http = { version = "0.6.1", features = ["trace"] }
|
||||||
tokio = { version = "1.37.0", features = ["full"] }
|
tokio = { version = "1.37.0", features = ["full"] }
|
||||||
indexmap = { version = "2.5.0", features = ["serde"] }
|
indexmap = { version = "2.5.0", features = ["serde"] }
|
||||||
zip = "2.2.0"
|
zip = "2.2.0"
|
||||||
|
|
|
||||||
|
|
@ -34,4 +34,4 @@ VOLUME [ "/app/data" ]
|
||||||
VOLUME [ "/app/logs" ]
|
VOLUME [ "/app/logs" ]
|
||||||
EXPOSE 6665/tcp
|
EXPOSE 6665/tcp
|
||||||
|
|
||||||
CMD [ "./sculptor" ]
|
ENTRYPOINT [ "./sculptor" ]
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# The Sculptor
|
# The Sculptor
|
||||||
|
|
||||||
[](https://github.com/shiroyashik/sculptor/actions/workflows/rust.yml)
|
[](https://github.com/shiroyashik/sculptor/actions/workflows/dev-release.yml)
|
||||||
|
|
||||||
Unofficial backend V2 for the Minecraft mod [Figura](https://github.com/FiguraMC/Figura).
|
Unofficial backend V2 for the Minecraft mod [Figura](https://github.com/FiguraMC/Figura).
|
||||||
|
|
||||||
|
|
|
||||||
17
build.rs
Normal file
17
build.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let output = match Command::new("git").args(&["rev-parse", "HEAD"]).output() {
|
||||||
|
Ok(d) => d,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Can't run git and get last commit due: {err:?}");
|
||||||
|
println!("cargo:rustc-env=GIT_HASH=0000000000000000000000000000000000000000");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut git_hash = String::from_utf8(output.stdout).unwrap();
|
||||||
|
if &git_hash == "HEAD" || git_hash.len() < 7 {
|
||||||
|
git_hash = String::from("0000000000000000000000000000000000000000");
|
||||||
|
}
|
||||||
|
println!("cargo:rustc-env=GIT_HASH={}", git_hash);
|
||||||
|
}
|
||||||
|
|
@ -5,9 +5,10 @@ pub const LOGS_ENV: &'static str = "LOGS_FOLDER";
|
||||||
pub const ASSETS_ENV: &'static str = "ASSETS_FOLDER";
|
pub const ASSETS_ENV: &'static str = "ASSETS_FOLDER";
|
||||||
pub const AVATARS_ENV: &'static str = "AVATARS_FOLDER";
|
pub const AVATARS_ENV: &'static str = "AVATARS_FOLDER";
|
||||||
|
|
||||||
// Sculptor update checker
|
// Instance info
|
||||||
pub const SCULPTOR_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
pub const SCULPTOR_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||||
pub const REPOSITORY: &'static str = "shiroyashik/sculptor";
|
pub const REPOSITORY: &'static str = "shiroyashik/sculptor";
|
||||||
|
pub const GIT_HASH: &'static str = env!("GIT_HASH", "Can't run git or build.rs failing!");
|
||||||
|
|
||||||
// reqwest parameters
|
// reqwest parameters
|
||||||
pub const USER_AGENT: &'static str = "reqwest";
|
pub const USER_AGENT: &'static str = "reqwest";
|
||||||
|
|
|
||||||
44
src/main.rs
44
src/main.rs
|
|
@ -35,12 +35,7 @@ use state::Config;
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
mod utils;
|
mod utils;
|
||||||
use utils::{
|
use utils::*;
|
||||||
check_updates, download_assets, get_commit_sha,
|
|
||||||
get_limit_as_bytes, get_log_file, get_path_to_assets_hash,
|
|
||||||
is_assets_outdated, remove_assets, update_advanced_users,
|
|
||||||
update_bans_from_minecraft, write_sha_to_file, FiguraVersions
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
|
|
@ -78,9 +73,10 @@ lazy_static! {
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
// 1. Set up env
|
||||||
let _ = dotenvy::dotenv();
|
let _ = dotenvy::dotenv();
|
||||||
// "trace,axum=info,tower_http=info,tokio=info,tungstenite=info,tokio_tungstenite=info",
|
|
||||||
|
|
||||||
|
// 2. Set up logging
|
||||||
let file_appender = tracing_appender::rolling::never(&*LOGS_VAR, get_log_file(&*LOGS_VAR));
|
let file_appender = tracing_appender::rolling::never(&*LOGS_VAR, get_log_file(&*LOGS_VAR));
|
||||||
let timer = ChronoLocal::new(String::from("%Y-%m-%dT%H:%M:%S%.3f%:z"));
|
let timer = ChronoLocal::new(String::from("%Y-%m-%dT%H:%M:%S%.3f%:z"));
|
||||||
|
|
||||||
|
|
@ -111,8 +107,34 @@ async fn main() -> Result<()> {
|
||||||
prev_hook(panic_info);
|
prev_hook(panic_info);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
tracing::info!("The Sculptor v{}{}", SCULPTOR_VERSION, check_updates(REPOSITORY, &SCULPTOR_VERSION).await?);
|
// 3. Display info about current instance and check updates
|
||||||
|
tracing::info!("The Sculptor v{SCULPTOR_VERSION}+{} ({REPOSITORY})", &GIT_HASH[..7]);
|
||||||
|
// let _ = check_updates(REPOSITORY, SCULPTOR_VERSION).await; // Currently, there is no need to do anything with the result of the function
|
||||||
|
|
||||||
|
match get_latest_version(REPOSITORY).await {
|
||||||
|
Ok(latest_version) => {
|
||||||
|
if latest_version > semver::Version::parse(SCULPTOR_VERSION).expect("SCULPTOR_VERSION does not match SemVer!") {
|
||||||
|
tracing::info!("Available new v{latest_version}! Check https://github.com/{REPOSITORY}/releases");
|
||||||
|
} else {
|
||||||
|
tracing::info!("Sculptor are up to date!");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Can't fetch Sculptor updates due: {e:?}");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Starting an app() that starts to serve. If app() returns true, the sculptor will be restarted. for future
|
||||||
|
loop {
|
||||||
|
if !app().await? {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn app() -> Result<bool> {
|
||||||
// Preparing for launch
|
// Preparing for launch
|
||||||
{
|
{
|
||||||
let path = PathBuf::from(&*AVATARS_VAR);
|
let path = PathBuf::from(&*AVATARS_VAR);
|
||||||
|
|
@ -212,8 +234,8 @@ async fn main() -> Result<()> {
|
||||||
axum::serve(listener, app)
|
axum::serve(listener, app)
|
||||||
.with_graceful_shutdown(shutdown_signal())
|
.with_graceful_shutdown(shutdown_signal())
|
||||||
.await?;
|
.await?;
|
||||||
tracing::info!("Serve stopped. Closing...");
|
tracing::info!("Serve stopped.");
|
||||||
Ok(())
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn shutdown_signal() {
|
async fn shutdown_signal() {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
use std::path::{self, PathBuf};
|
use std::path::{self, PathBuf};
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::bail;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::{fs::{self, File}, io::{AsyncReadExt as _, AsyncWriteExt as _}};
|
use tokio::{fs::{self, File}, io::{AsyncReadExt as _, AsyncWriteExt as _}};
|
||||||
use tracing::error;
|
|
||||||
|
|
||||||
use crate::{ASSETS_VAR, FIGURA_ASSETS_ZIP_URL, FIGURA_RELEASES_URL, TIMEOUT, USER_AGENT};
|
use crate::{ASSETS_VAR, FIGURA_ASSETS_ZIP_URL, FIGURA_RELEASES_URL, TIMEOUT, USER_AGENT};
|
||||||
|
|
||||||
|
|
@ -14,7 +13,7 @@ struct Tag {
|
||||||
name: String
|
name: String
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_latest_version(repo: &str, current_version: Version) -> anyhow::Result<Option<String>> {
|
pub async fn get_latest_version(repo: &str) -> anyhow::Result<Version> {
|
||||||
let url = format!("https://api.github.com/repos/{repo}/tags");
|
let url = format!("https://api.github.com/repos/{repo}/tags");
|
||||||
let client = Client::builder().timeout(TIMEOUT).user_agent(USER_AGENT).build().unwrap();
|
let client = Client::builder().timeout(TIMEOUT).user_agent(USER_AGENT).build().unwrap();
|
||||||
let response = client.get(&url).send().await?;
|
let response = client.get(&url).send().await?;
|
||||||
|
|
@ -31,32 +30,12 @@ async fn get_latest_version(repo: &str, current_version: Version) -> anyhow::Res
|
||||||
})
|
})
|
||||||
.max();
|
.max();
|
||||||
if let Some(latest_version) = latest_tag {
|
if let Some(latest_version) = latest_tag {
|
||||||
if latest_version > current_version {
|
Ok(latest_version)
|
||||||
Ok(Some(format!("Available new v{latest_version}")))
|
|
||||||
} else {
|
|
||||||
Ok(Some("Up to date".to_string()))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Can't find version tags!"))
|
bail!("Can't find version tags!")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Response status code: {}", response.status().as_u16()))
|
bail!("Response status code: {}", response.status().as_u16())
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn check_updates(repo: &str, current_version: &str) -> anyhow::Result<String> {
|
|
||||||
let current_version = semver::Version::parse(¤t_version)?;
|
|
||||||
|
|
||||||
match get_latest_version(repo, current_version).await {
|
|
||||||
Ok(d) => if let Some(text) = d {
|
|
||||||
Ok(format!(" - {text}!"))
|
|
||||||
} else {
|
|
||||||
Ok(String::new())
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
error!("Can't fetch updates: {e:?}");
|
|
||||||
Ok(String::new())
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,7 +58,7 @@ pub async fn get_figura_versions() -> anyhow::Result<FiguraVersions> {
|
||||||
let multiple_releases: Vec<Release> = response.json().await?;
|
let multiple_releases: Vec<Release> = response.json().await?;
|
||||||
for release in multiple_releases {
|
for release in multiple_releases {
|
||||||
let tag_ver = if let Ok(res) = Version::parse(&release.tag_name) { res } else {
|
let tag_ver = if let Ok(res) = Version::parse(&release.tag_name) { res } else {
|
||||||
error!("Incorrect tag name! {release:?}");
|
tracing::error!("Incorrect tag name! {release:?}");
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if release.prerelease {
|
if release.prerelease {
|
||||||
|
|
@ -98,7 +77,7 @@ pub async fn get_figura_versions() -> anyhow::Result<FiguraVersions> {
|
||||||
// Stop
|
// Stop
|
||||||
Ok(FiguraVersions { release: release_ver.to_string(), prerelease: prerelease_ver.to_string() })
|
Ok(FiguraVersions { release: release_ver.to_string(), prerelease: prerelease_ver.to_string() })
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Response status code: {}", response.status().as_u16()))
|
bail!("Response status code: {}", response.status().as_u16())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue