[+] Docker

This commit is contained in:
2026-05-08 06:29:13 +00:00
parent d19b061f7c
commit b7f3404f99
8 changed files with 151 additions and 4 deletions
+4
View File
@@ -0,0 +1,4 @@
/target
/.git
/.github
.env
+26
View File
@@ -0,0 +1,26 @@
FROM rust:1-slim-bookworm AS build
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
COPY src ./src
RUN cargo build --release --locked
FROM debian:bookworm-slim AS runtime
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates git nano openssh-client vim \
&& rm -rf /var/lib/apt/lists/* \
&& useradd --uid 10001 --create-home --home-dir /home/refray --shell /usr/sbin/nologin refray \
&& mkdir -p /data \
&& chown -R refray:refray /data
COPY --from=build /app/target/release/refray /usr/local/bin/refray
USER refray
WORKDIR /data
ENV XDG_CONFIG_HOME=/data/config \
XDG_CACHE_HOME=/data/cache
EXPOSE 8787
ENTRYPOINT ["refray"]
CMD ["serve", "--listen", "0.0.0.0:8787"]
+21 -1
View File
@@ -1,6 +1,6 @@
# refray # refray
A tool to keep ALL of your repos in sync across ALL git platforms, while being able to work from any one of them. A tool to keep your repos in sync across all git platforms, while being able to work from everywhere all at once.
Created becasue github is so unusable and unreliable and I want to leave, but I don't want to leave the community behind. Created becasue github is so unusable and unreliable and I want to leave, but I don't want to leave the community behind.
@@ -22,6 +22,26 @@ Supported platforms: GitHub, GitLab, Gitea, Forgejo
Go to the [releases page](https://github.com/MaigoLabs/refray/releases), find the latest release, and download the appropriate binary for your platform. Go to the [releases page](https://github.com/MaigoLabs/refray/releases), find the latest release, and download the appropriate binary for your platform.
### Option 3. Docker Compose
Run config wizard:
```sh
docker compose run --rm refray config
```
Start the webhook receiver as a service:
```sh
docker compose up -d --build
```
To edit config manually:
```sh
docker compose run --rm --entrypoint nano refray /data/config/refray/config.toml
```
## Configure ## Configure
Run the interactive configuration wizard: Run the interactive configuration wizard:
+14
View File
@@ -0,0 +1,14 @@
services:
refray:
build: .
image: refray:local
command: ["serve", "--listen", "0.0.0.0:8787"]
ports:
- "8787:8787"
volumes:
- refray-data:/data
restart: unless-stopped
volumes:
refray-data:
name: refray-data
+25 -2
View File
@@ -37,7 +37,7 @@ struct ParsedProfileUrl {
namespace: String, namespace: String,
} }
pub fn run_config_wizard(path: &Path) -> Result<()> { pub fn run_config_wizard(path: &Path) -> Result<ConfigWizardOutcome> {
let existing_config = path.exists(); let existing_config = path.exists();
let mut config = Config::load_or_default(path)?; let mut config = Config::load_or_default(path)?;
let theme = ColorfulTheme::default(); let theme = ColorfulTheme::default();
@@ -85,7 +85,17 @@ pub fn run_config_wizard(path: &Path) -> Result<()> {
style("saved").green().bold(), style("saved").green().bold(),
style(path.display()).cyan() style(path.display()).cyan()
); );
Ok(()) let run_full_sync_now = prompt_run_full_sync_now_styled(&config, &theme)?;
Ok(ConfigWizardOutcome {
config,
run_full_sync_now,
})
}
#[derive(Clone, Debug)]
pub struct ConfigWizardOutcome {
pub config: Config,
pub run_full_sync_now: bool,
} }
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -389,6 +399,19 @@ fn prompt_wizard_action_styled(theme: &ColorfulTheme) -> Result<WizardAction> {
}) })
} }
fn prompt_run_full_sync_now_styled(config: &Config, theme: &ColorfulTheme) -> Result<bool> {
if config.mirrors.is_empty() {
return Ok(false);
}
println!();
Confirm::with_theme(theme)
.with_prompt("Run full sync now?")
.default(false)
.interact()
.map_err(Into::into)
}
fn edit_sync_group_styled(config: &mut Config, theme: &ColorfulTheme) -> Result<bool> { fn edit_sync_group_styled(config: &mut Config, theme: &ColorfulTheme) -> Result<bool> {
if config.mirrors.is_empty() { if config.mirrors.is_empty() {
println!("{}", style("No sync groups to edit.").yellow()); println!("{}", style("No sync groups to edit.").yellow());
+8 -1
View File
@@ -146,7 +146,14 @@ fn main() -> Result<()> {
let config_path = cli.config.unwrap_or_else(default_config_path); let config_path = cli.config.unwrap_or_else(default_config_path);
match cli.command { match cli.command {
Command::Config => interactive::run_config_wizard(&config_path), Command::Config => {
let outcome = interactive::run_config_wizard(&config_path)?;
if outcome.run_full_sync_now {
sync_all(&outcome.config, SyncOptions::default())
} else {
Ok(())
}
}
Command::Sync(command) => { Command::Sync(command) => {
let config = load_config(&config_path)?; let config = load_config(&config_path)?;
sync_all( sync_all(
+37
View File
@@ -219,6 +219,43 @@ fn wizard_starts_existing_config_at_sync_group_menu() {
assert!(!output.contains("Profile/org URL:")); assert!(!output.contains("Profile/org URL:"));
} }
#[test]
fn wizard_can_ask_to_run_full_sync_after_config() {
let config = Config {
sites: Vec::new(),
mirrors: vec![MirrorConfig {
name: "sync-1".to_string(),
endpoints: Vec::new(),
create_missing: true,
visibility: Visibility::Private,
allow_force: false,
conflict_resolution: ConflictResolutionStrategy::Fail,
}],
webhook: None,
};
let mut reader = Cursor::new(b"y\n".as_slice());
let mut output = Vec::new();
let run_full_sync =
prompt_run_full_sync_now_with_io(&config, &mut reader, &mut output).unwrap();
assert!(run_full_sync);
let output = String::from_utf8(output).unwrap();
assert!(output.contains("Run full sync now? [y/N]:"));
}
#[test]
fn wizard_skips_full_sync_prompt_without_sync_groups() {
let mut reader = Cursor::new(b"".as_slice());
let mut output = Vec::new();
let run_full_sync =
prompt_run_full_sync_now_with_io(&Config::default(), &mut reader, &mut output).unwrap();
assert!(!run_full_sync);
assert!(output.is_empty());
}
#[test] #[test]
fn wizard_edits_existing_sync_group_from_menu() { fn wizard_edits_existing_sync_group_from_menu() {
let config = Config { let config = Config {
+16
View File
@@ -41,6 +41,22 @@ where
Ok(config) Ok(config)
} }
pub fn prompt_run_full_sync_now_with_io<R, W>(
config: &Config,
reader: &mut R,
writer: &mut W,
) -> Result<bool>
where
R: BufRead,
W: Write,
{
if config.mirrors.is_empty() {
return Ok(false);
}
prompt_bool(reader, writer, "Run full sync now?", false)
}
fn add_sync_group<R, W>(reader: &mut R, writer: &mut W, config: &mut Config) -> Result<()> fn add_sync_group<R, W>(reader: &mut R, writer: &mut W, config: &mut Config) -> Result<()>
where where
R: BufRead, R: BufRead,