mirror of
https://github.com/dexoron/dcr.git
synced 2026-10-22 18:09:37 +00:00
Compare commits
12 Commits
60d7420929
...
v0.8.4
| Author | SHA1 | Date | |
|---|---|---|---|
| 3bd91e85f9 | |||
| 9bf1346d1b | |||
| f29322a547 | |||
| 689fea7465 | |||
| 1dcc8e614a | |||
| a3f8444c20 | |||
| 1f3fc62cb6 | |||
| f8d8c95912 | |||
| 9e07f70c78 | |||
| 0376eded3f | |||
| 803d5891ce | |||
| 97db82e4f8 |
14
.github/workflows/trigger-site-build.yml
vendored
Normal file
14
.github/workflows/trigger-site-build.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
name: Trigger site rebuild
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
paths:
|
||||||
|
- 'docs/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
trigger:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Trigger Cloudflare Pages build
|
||||||
|
run: curl -sS -X POST "${{ secrets.CF_PAGES_DEPLOY_HOOK }}"
|
||||||
26
CHANGELOG.md
26
CHANGELOG.md
@@ -1,31 +1,5 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [v0.8.5] - 2026-08-08 "Фиксация версии dcr-version и предупреждения совместимости / Config Version Pinning & Compatibility Warnings"
|
|
||||||
|
|
||||||
### RU
|
|
||||||
|
|
||||||
**Добавлено:**
|
|
||||||
|
|
||||||
- **`package.dcr-version`** — опциональный pin версии CLI в `dcr.toml`.
|
|
||||||
`dcr new` / `dcr init` пишут текущую версию. При загрузке конфига:
|
|
||||||
- нет / пустой pin → warn «добавь dcr-version»;
|
|
||||||
- dcr старше pin → warn «обнови dcr»;
|
|
||||||
- dcr новее pin → warn «проверь changelog, подними pin»;
|
|
||||||
- равенство — тишина.
|
|
||||||
Команды не падают, только предупреждение (раз на path за процесс).
|
|
||||||
|
|
||||||
### EN
|
|
||||||
|
|
||||||
**Added:**
|
|
||||||
|
|
||||||
- **`package.dcr-version`** — optional CLI version pin in `dcr.toml`.
|
|
||||||
`dcr new` / `dcr init` write the current version. On config load:
|
|
||||||
- missing / empty pin → warn "add dcr-version";
|
|
||||||
- dcr older than pin → warn "update dcr";
|
|
||||||
- dcr newer than pin → warn "check changelog, update pin";
|
|
||||||
- equal → silent.
|
|
||||||
Commands do not fail, warning only (once per path per process).
|
|
||||||
|
|
||||||
## [0.8.4] - 2026-08-04 "Инфраструктура .dcr/ и интеграция с IDE / Internal .dcr/ Layout & IDE Integration"
|
## [0.8.4] - 2026-08-04 "Инфраструктура .dcr/ и интеграция с IDE / Internal .dcr/ Layout & IDE Integration"
|
||||||
|
|
||||||
### RU
|
### RU
|
||||||
|
|||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -154,7 +154,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dcr"
|
name = "dcr"
|
||||||
version = "0.8.5"
|
version = "0.8.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
"fatfs",
|
"fatfs",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dcr"
|
name = "dcr"
|
||||||
version = "0.8.5"
|
version = "0.8.4"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
description = "DCR is a utility for managing C/C++ projects in a Cargo-like style."
|
description = "DCR is a utility for managing C/C++ projects in a Cargo-like style."
|
||||||
|
|||||||
3
crowdin.yml
Normal file
3
crowdin.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
files:
|
||||||
|
- source: /docs/**/*.mdx
|
||||||
|
translation: /docs-crowdin-export/%locale%/%original_path%/%original_file_name%.%file_extension%
|
||||||
@@ -4,16 +4,6 @@ sidebar_label: Changelog
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## 0.8.5 (2026-08-08)
|
|
||||||
|
|
||||||
- **`package.dcr-version`** — optional CLI version pin in `dcr.toml`.
|
|
||||||
`dcr new` / `dcr init` write the current version. On config load:
|
|
||||||
- missing / empty pin → warn "add dcr-version";
|
|
||||||
- dcr older than pin → warn "update dcr";
|
|
||||||
- dcr newer than pin → warn "check changelog, update pin";
|
|
||||||
- equal → silent.
|
|
||||||
Commands do not fail, warning only (once per path per process).
|
|
||||||
|
|
||||||
## 0.8.4 (2026-08-04)
|
## 0.8.4 (2026-08-04)
|
||||||
|
|
||||||
**Added:**
|
**Added:**
|
||||||
|
|||||||
@@ -43,8 +43,7 @@ Main project configuration file. Located at the project root.
|
|||||||
| Field | Required | Description |
|
| Field | Required | Description |
|
||||||
|-------|----------|-------------|
|
|-------|----------|-------------|
|
||||||
| `name` | yes | Project name |
|
| `name` | yes | Project name |
|
||||||
| `version` | yes | Semantic version of the **project** |
|
| `version` | yes | Semantic version |
|
||||||
| `dcr-version` | no | Semver of the **dcr tool** this project targets. Written by `dcr new` / `dcr init` to the current tool version. |
|
|
||||||
| `type` | no | `app`, `lib`, `none` (defaults to `"none"`) |
|
| `type` | no | `app`, `lib`, `none` (defaults to `"none"`) |
|
||||||
| `license` | no | SPDX license identifier |
|
| `license` | no | SPDX license identifier |
|
||||||
| `author` | no | Author |
|
| `author` | no | Author |
|
||||||
@@ -53,26 +52,11 @@ Main project configuration file. Located at the project root.
|
|||||||
[package]
|
[package]
|
||||||
name = "my-app"
|
name = "my-app"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dcr-version = "0.8.4"
|
|
||||||
type = "app"
|
type = "app"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
author = "John Doe"
|
author = "John Doe"
|
||||||
```
|
```
|
||||||
|
|
||||||
### `package.dcr-version`
|
|
||||||
|
|
||||||
Optional pin of the DCR CLI the project was written for. On every `dcr.toml` load:
|
|
||||||
|
|
||||||
| Situation | Behaviour |
|
|
||||||
|-----------|-----------|
|
|
||||||
| Field missing / empty | Warning: add `dcr-version` (set by `dcr new` / `dcr init`) |
|
|
||||||
| Running dcr **older** than pin | Warning: upgrade dcr (`dcr --update`) |
|
|
||||||
| Running dcr **newer** than pin | Warning: review changelog, bump the pin when ready |
|
|
||||||
| Equal | Silent |
|
|
||||||
| Invalid semver | Warning once, field ignored |
|
|
||||||
|
|
||||||
Never fails the command — warnings only. Pre-release suffixes (`1.0.0-dev`) are ignored for comparison (`1.0.0-dev` ≡ `1.0.0`).
|
|
||||||
|
|
||||||
## [build]
|
## [build]
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|||||||
@@ -154,11 +154,6 @@ Project name (required).
|
|||||||
.B package.version
|
.B package.version
|
||||||
Project version (default: \fB0.1.0\fR).
|
Project version (default: \fB0.1.0\fR).
|
||||||
.TP
|
.TP
|
||||||
.B package.dcr\-version
|
|
||||||
Semver of the DCR tool this project targets. Set automatically by
|
|
||||||
\fBdcr new\fR / \fBdcr init\fR. Missing pin, or an installed \fBdcr\fR older/newer
|
|
||||||
than the pin, prints a warning (commands still run).
|
|
||||||
.TP
|
|
||||||
.B build.language
|
.B build.language
|
||||||
Programming language: \fBc\fR or \fBc++\fR (default: \fBc\fR).
|
Programming language: \fBc\fR or \fBc++\fR (default: \fBc\fR).
|
||||||
.TP
|
.TP
|
||||||
|
|||||||
@@ -132,17 +132,11 @@ pub struct ArchiveLayout {
|
|||||||
pub to: String,
|
pub to: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Package metadata section (name, version, type, dcr-version).
|
/// Package metadata section (name, version, type).
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct PackageConfig {
|
pub struct PackageConfig {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub version: String,
|
pub version: String,
|
||||||
/// Minimum/target DCR tool version this project was authored for (`package.dcr-version`).
|
|
||||||
///
|
|
||||||
/// Compared against the running `dcr` binary (`CARGO_PKG_VERSION`) on config load.
|
|
||||||
/// Missing or empty means "no pin" — no warning is emitted.
|
|
||||||
#[serde(default, rename = "dcr-version")]
|
|
||||||
pub dcr_version: Option<String>,
|
|
||||||
#[serde(default, rename = "type")]
|
#[serde(default, rename = "type")]
|
||||||
pub pkg_type: Option<String>,
|
pub pkg_type: Option<String>,
|
||||||
}
|
}
|
||||||
@@ -264,7 +258,6 @@ impl Config {
|
|||||||
doc,
|
doc,
|
||||||
};
|
};
|
||||||
cfg.validate()?;
|
cfg.validate()?;
|
||||||
cfg.warn_dcr_version_mismatch();
|
|
||||||
Ok(cfg)
|
Ok(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +276,6 @@ impl Config {
|
|||||||
doc,
|
doc,
|
||||||
};
|
};
|
||||||
cfg.validate()?;
|
cfg.validate()?;
|
||||||
cfg.warn_dcr_version_mismatch();
|
|
||||||
Ok(cfg)
|
Ok(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,68 +291,6 @@ impl Config {
|
|||||||
self.typed.package.as_ref()
|
self.typed.package.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Warn once per config path about `package.dcr-version` vs this `dcr` binary.
|
|
||||||
///
|
|
||||||
/// - Missing/`""` pin → recommend adding `dcr-version`.
|
|
||||||
/// - Tool older than pin → project may use features this binary does not support.
|
|
||||||
/// - Tool newer than pin → project may rely on outdated defaults; consider bumping the pin.
|
|
||||||
///
|
|
||||||
/// Never fails the load; warnings only. Equal pin is silent.
|
|
||||||
fn warn_dcr_version_mismatch(&self) {
|
|
||||||
let Some(pkg) = self.typed.package.as_ref() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let path_key = self
|
|
||||||
.path
|
|
||||||
.canonicalize()
|
|
||||||
.unwrap_or_else(|_| self.path.clone())
|
|
||||||
.to_string_lossy()
|
|
||||||
.into_owned();
|
|
||||||
if !dcr_version_warn_once(&path_key) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let tool = env!("CARGO_PKG_VERSION");
|
|
||||||
let required = pkg
|
|
||||||
.dcr_version
|
|
||||||
.as_deref()
|
|
||||||
.map(str::trim)
|
|
||||||
.filter(|s| !s.is_empty());
|
|
||||||
let Some(required) = required else {
|
|
||||||
crate::utils::log::warn(&format!(
|
|
||||||
"package.dcr-version is missing in {}. Add e.g. dcr-version = \"{tool}\" \
|
|
||||||
so collaborators know which dcr this project targets \
|
|
||||||
(written automatically by `dcr new` / `dcr init`).",
|
|
||||||
self.path.display()
|
|
||||||
));
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
match crate::utils::build::compare_semver(tool, required) {
|
|
||||||
Some(std::cmp::Ordering::Less) => {
|
|
||||||
crate::utils::log::warn(&format!(
|
|
||||||
"this project requires dcr {required} (package.dcr-version), \
|
|
||||||
but you are running dcr {tool}. Some features may be missing or misbehave. \
|
|
||||||
Upgrade dcr (e.g. `dcr --update`) or lower package.dcr-version if intentional."
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Some(std::cmp::Ordering::Greater) => {
|
|
||||||
crate::utils::log::warn(&format!(
|
|
||||||
"this project pins dcr {required} (package.dcr-version), \
|
|
||||||
but you are running newer dcr {tool}. Config/defaults may be outdated. \
|
|
||||||
Review the changelog and bump package.dcr-version when the project is verified."
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Some(std::cmp::Ordering::Equal) => {}
|
|
||||||
None => {
|
|
||||||
crate::utils::log::warn(&format!(
|
|
||||||
"package.dcr-version = \"{required}\" is not a valid semver (expected X.Y.Z); ignoring"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
/// Returns the build configuration if present.
|
/// Returns the build configuration if present.
|
||||||
pub fn build_config(&self) -> Option<&BuildConfig> {
|
pub fn build_config(&self) -> Option<&BuildConfig> {
|
||||||
@@ -835,13 +765,11 @@ fn default_toml_text() -> String {
|
|||||||
.ok()
|
.ok()
|
||||||
.and_then(|p| p.file_name().map(|v| v.to_string_lossy().to_string()))
|
.and_then(|p| p.file_name().map(|v| v.to_string_lossy().to_string()))
|
||||||
.unwrap_or_else(|| "project".to_string());
|
.unwrap_or_else(|| "project".to_string());
|
||||||
let dcr_version = env!("CARGO_PKG_VERSION");
|
|
||||||
|
|
||||||
format!(
|
format!(
|
||||||
"[package]\n\
|
"[package]\n\
|
||||||
name = \"{name}\"\n\
|
name = \"{name}\"\n\
|
||||||
version = \"{DEFAULT_VERSION}\"\n\
|
version = \"{DEFAULT_VERSION}\"\n\
|
||||||
dcr-version = \"{dcr_version}\"\n\
|
|
||||||
type = \"none\"\n\
|
type = \"none\"\n\
|
||||||
description = \"\"\n\
|
description = \"\"\n\
|
||||||
author = \"\"\n\
|
author = \"\"\n\
|
||||||
@@ -857,17 +785,6 @@ fn default_toml_text() -> String {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dcr_version_warn_once(path_key: &str) -> bool {
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::sync::{Mutex, OnceLock};
|
|
||||||
static WARNED: OnceLock<Mutex<HashSet<String>>> = OnceLock::new();
|
|
||||||
let set = WARNED.get_or_init(|| Mutex::new(HashSet::new()));
|
|
||||||
let Ok(mut guard) = set.lock() else {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
guard.insert(path_key.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_doc_path(doc: &mut DocumentMut, path: &[&str], value: &Value) -> Result<(), ConfigError> {
|
fn set_doc_path(doc: &mut DocumentMut, path: &[&str], value: &Value) -> Result<(), ConfigError> {
|
||||||
let mut current = doc.as_table_mut();
|
let mut current = doc.as_table_mut();
|
||||||
for &key in &path[..path.len().saturating_sub(1)] {
|
for &key in &path[..path.len().saturating_sub(1)] {
|
||||||
@@ -1020,21 +937,11 @@ mod tests {
|
|||||||
let dir = temp_dir("typed_config");
|
let dir = temp_dir("typed_config");
|
||||||
let path = write_toml_file(
|
let path = write_toml_file(
|
||||||
&dir,
|
&dir,
|
||||||
"[package]\nname = \"typed\"\nversion = \"1.2.3\"\ndcr-version = \"0.8.4\"\ntype = \"lib\"\n\n[build]\nlanguage = [\"c\", \"c++\"]\nstandard = \"c11\"\ncompiler = \"clang\"\nkind = \"staticlib\"\ncflags = [\"-Wall\"]\n\n[dependencies]\nfoo = \"1.0.0\"\n",
|
"[package]\nname = \"typed\"\nversion = \"1.2.3\"\ntype = \"lib\"\n\n[build]\nlanguage = [\"c\", \"c++\"]\nstandard = \"c11\"\ncompiler = \"clang\"\nkind = \"staticlib\"\ncflags = [\"-Wall\"]\n\n[dependencies]\nfoo = \"1.0.0\"\n",
|
||||||
);
|
);
|
||||||
let config = Config::open(&path.to_string_lossy()).unwrap();
|
let config = Config::open(&path.to_string_lossy()).unwrap();
|
||||||
assert_eq!(config.package().unwrap().name, "typed");
|
assert_eq!(config.package().unwrap().name, "typed");
|
||||||
assert_eq!(config.typed().package.as_ref().unwrap().version, "1.2.3");
|
assert_eq!(config.typed().package.as_ref().unwrap().version, "1.2.3");
|
||||||
assert_eq!(
|
|
||||||
config
|
|
||||||
.typed()
|
|
||||||
.package
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.dcr_version
|
|
||||||
.as_deref(),
|
|
||||||
Some("0.8.4")
|
|
||||||
);
|
|
||||||
assert_eq!(config.build_config().unwrap().compiler, "clang");
|
assert_eq!(config.build_config().unwrap().compiler, "clang");
|
||||||
assert_eq!(config.build_config().unwrap().cflags, ["-Wall"]);
|
assert_eq!(config.build_config().unwrap().cflags, ["-Wall"]);
|
||||||
assert!(config.typed().dependencies.contains_key("foo"));
|
assert!(config.typed().dependencies.contains_key("foo"));
|
||||||
|
|||||||
@@ -31,39 +31,6 @@ pub struct VersionInfo {
|
|||||||
pub suffix_dash: String,
|
pub suffix_dash: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compares two semver-like strings (`X.Y.Z` with optional `-suffix`, suffix ignored).
|
|
||||||
///
|
|
||||||
/// Returns `None` if either side has no parseable numeric major component.
|
|
||||||
/// Ordering is tool-centric: `compare_semver(tool, required)` → `Less` means tool is older.
|
|
||||||
pub fn compare_semver(left: &str, right: &str) -> Option<std::cmp::Ordering> {
|
|
||||||
let l = semver_numeric_parts(left)?;
|
|
||||||
let r = semver_numeric_parts(right)?;
|
|
||||||
Some(l.cmp(&r))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn semver_numeric_parts(version: &str) -> Option<(u64, u64, u64)> {
|
|
||||||
let base = version
|
|
||||||
.trim()
|
|
||||||
.split_once('-')
|
|
||||||
.map(|(h, _)| h)
|
|
||||||
.unwrap_or(version.trim());
|
|
||||||
if base.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut parts = base.split('.');
|
|
||||||
let major = parts.next()?.parse::<u64>().ok()?;
|
|
||||||
let minor = parts.next().unwrap_or("0").parse::<u64>().unwrap_or(0);
|
|
||||||
let patch = parts
|
|
||||||
.next()
|
|
||||||
.unwrap_or("0")
|
|
||||||
.chars()
|
|
||||||
.take_while(|c| c.is_ascii_digit())
|
|
||||||
.collect::<String>()
|
|
||||||
.parse::<u64>()
|
|
||||||
.unwrap_or(0);
|
|
||||||
Some((major, minor, patch))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses a version string into VersionInfo struct.
|
/// Parses a version string into VersionInfo struct.
|
||||||
pub fn parse_version_info(version: &str) -> VersionInfo {
|
pub fn parse_version_info(version: &str) -> VersionInfo {
|
||||||
let mut full = version.trim().to_string();
|
let mut full = version.trim().to_string();
|
||||||
@@ -794,16 +761,6 @@ mod tests {
|
|||||||
assert_eq!(info.suffix_dash, "-beta");
|
assert_eq!(info.suffix_dash, "-beta");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn compare_semver_orders_and_ignores_prerelease() {
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
assert_eq!(compare_semver("0.8.4", "0.8.4"), Some(Ordering::Equal));
|
|
||||||
assert_eq!(compare_semver("0.8.3", "0.8.4"), Some(Ordering::Less));
|
|
||||||
assert_eq!(compare_semver("0.9.0", "0.8.4"), Some(Ordering::Greater));
|
|
||||||
assert_eq!(compare_semver("1.0.0-dev", "1.0.0"), Some(Ordering::Equal));
|
|
||||||
assert_eq!(compare_semver("not-a-version", "0.1.0"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test for normalize_target_with_profile function.
|
/// Test for normalize_target_with_profile function.
|
||||||
#[test]
|
#[test]
|
||||||
fn normalize_target_with_profile() {
|
fn normalize_target_with_profile() {
|
||||||
|
|||||||
Reference in New Issue
Block a user