Skip to content

Commit 7f81148

Browse files
fix: install linux arm64 cross compiler in release ci
1 parent 73fa67f commit 7f81148

5 files changed

Lines changed: 143 additions & 6 deletions

File tree

.github/workflows/release.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ jobs:
4949
# - { rust_target: "i686-pc-windows-msvc", os: "windows-latest", ext: ".exe", name: "windows", arch: "x86" } # edit me
5050
steps:
5151
- uses: actions/checkout@v4
52+
- name: Install Linux aarch64 cross toolchain
53+
if: matrix.rust_target == 'aarch64-unknown-linux-gnu'
54+
run: |
55+
sudo apt-get update
56+
sudo apt-get install -y gcc-aarch64-linux-gnu
57+
- name: Configure Linux aarch64 linker
58+
if: matrix.rust_target == 'aarch64-unknown-linux-gnu'
59+
run: echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> "$GITHUB_ENV"
5260
- name: Build otf-release
5361
run: rustup target add ${{ matrix.rust_target }} && cargo build --release --target ${{ matrix.rust_target }} # edit me: cross-compile with ${{ matrix.rust_target }}
5462
- uses: actions/upload-artifact@v4

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/adapters/src/generic.rs

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
//! - **Publish** is an optional shell command (`publish`, e.g. `npx jsr publish`). When present
1111
//! the package publishes through `otf-release publish` (which then tags + makes the GitHub
1212
//! Release); when absent the package is build-only.
13-
//! - **No dependency graph / lockfile / ranges.**
13+
//! - **No dependency graph / ranges.** A generic package that versions a root `Cargo.toml`
14+
//! refreshes `Cargo.lock` after version writes so Rust build-only releases stay consistent.
1415
1516
use std::path::{Path, PathBuf};
1617
use std::process::Command;
@@ -21,6 +22,8 @@ use toml_edit::{value, DocumentMut, Item};
2122

2223
use otf_release_core::adapter::{Adapter, Bump, DepKind, Pkg};
2324

25+
use crate::command::{CommandRunner, SystemRunner};
26+
2427
/// One generic project, as configured in `release.toml`.
2528
#[derive(Debug, Clone)]
2629
pub struct GenericPkg {
@@ -37,13 +40,27 @@ pub struct GenericPkg {
3740
pub struct GenericAdapter {
3841
root: PathBuf,
3942
packages: Vec<GenericPkg>,
43+
runner: Box<dyn CommandRunner>,
4044
}
4145

4246
impl GenericAdapter {
4347
pub fn new(root: impl Into<PathBuf>, packages: Vec<GenericPkg>) -> Self {
4448
Self {
4549
root: root.into(),
4650
packages,
51+
runner: Box::new(SystemRunner),
52+
}
53+
}
54+
55+
pub fn with_runner(
56+
root: impl Into<PathBuf>,
57+
packages: Vec<GenericPkg>,
58+
runner: Box<dyn CommandRunner>,
59+
) -> Self {
60+
Self {
61+
root: root.into(),
62+
packages,
63+
runner,
4764
}
4865
}
4966

@@ -57,6 +74,15 @@ impl GenericAdapter {
5774
fn manifest_path(&self, pkg: &GenericPkg) -> PathBuf {
5875
self.root.join(&pkg.manifest)
5976
}
77+
78+
fn updates_cargo_lockfile(&self, root: &Path) -> bool {
79+
root.join("Cargo.lock").exists()
80+
&& self.packages.iter().any(|pkg| {
81+
pkg.manifest
82+
.file_name()
83+
.is_some_and(|name| name == "Cargo.toml")
84+
})
85+
}
6086
}
6187

6288
/// Find the version value for `field` in manifest `text`. Matches `"field"…:…"value"` (JSON) and
@@ -276,7 +302,13 @@ impl Adapter for GenericAdapter {
276302
Ok(())
277303
}
278304

279-
fn update_lockfile(&self, _root: &Path) -> Result<()> {
305+
fn update_lockfile(&self, root: &Path) -> Result<()> {
306+
if self.updates_cargo_lockfile(root) {
307+
let out = self.runner.run("cargo", &["update", "--workspace"], root)?;
308+
if !out.success {
309+
bail!("`cargo update --workspace` failed:\n{}", out.stderr);
310+
}
311+
}
280312
Ok(())
281313
}
282314

@@ -327,6 +359,40 @@ impl Adapter for GenericAdapter {
327359
#[cfg(test)]
328360
mod tests {
329361
use super::*;
362+
use crate::command::{CommandOutput, CommandRunner};
363+
use std::sync::{Arc, Mutex};
364+
365+
type Calls = Arc<Mutex<Vec<(String, Vec<String>, PathBuf)>>>;
366+
367+
#[derive(Clone)]
368+
struct FakeRunner {
369+
out: CommandOutput,
370+
calls: Calls,
371+
}
372+
373+
impl FakeRunner {
374+
fn new(success: bool, stdout: &str, stderr: &str) -> Self {
375+
Self {
376+
out: CommandOutput {
377+
success,
378+
stdout: stdout.into(),
379+
stderr: stderr.into(),
380+
},
381+
calls: Arc::new(Mutex::new(Vec::new())),
382+
}
383+
}
384+
}
385+
386+
impl CommandRunner for FakeRunner {
387+
fn run(&self, program: &str, args: &[&str], cwd: &Path) -> Result<CommandOutput> {
388+
self.calls.lock().unwrap().push((
389+
program.to_string(),
390+
args.iter().map(|arg| arg.to_string()).collect(),
391+
cwd.to_path_buf(),
392+
));
393+
Ok(self.out.clone())
394+
}
395+
}
330396

331397
fn pkg(name: &str, manifest: &str, publish: Option<&str>) -> GenericPkg {
332398
GenericPkg {
@@ -390,6 +456,49 @@ mod tests {
390456
assert_eq!(a.discover_packages().unwrap()[0].version, "0.4.0");
391457
}
392458

459+
#[test]
460+
fn cargo_toml_manifest_refreshes_cargo_lockfile() {
461+
let tmp = tempfile::tempdir().unwrap();
462+
std::fs::write(tmp.path().join("Cargo.toml"), "version = \"0.1.0\"\n").unwrap();
463+
std::fs::write(tmp.path().join("Cargo.lock"), "").unwrap();
464+
let runner = FakeRunner::new(true, "", "");
465+
let calls = runner.calls.clone();
466+
let a = GenericAdapter::with_runner(
467+
tmp.path(),
468+
vec![pkg("x", "Cargo.toml", None)],
469+
Box::new(runner),
470+
);
471+
472+
a.update_lockfile(tmp.path()).unwrap();
473+
474+
assert_eq!(
475+
*calls.lock().unwrap(),
476+
vec![(
477+
"cargo".to_string(),
478+
vec!["update".to_string(), "--workspace".to_string()],
479+
tmp.path().to_path_buf()
480+
)]
481+
);
482+
}
483+
484+
#[test]
485+
fn non_cargo_manifest_does_not_refresh_lockfile() {
486+
let tmp = tempfile::tempdir().unwrap();
487+
std::fs::write(tmp.path().join("deno.json"), "{\"version\":\"1.0.0\"}").unwrap();
488+
std::fs::write(tmp.path().join("Cargo.lock"), "").unwrap();
489+
let runner = FakeRunner::new(true, "", "");
490+
let calls = runner.calls.clone();
491+
let a = GenericAdapter::with_runner(
492+
tmp.path(),
493+
vec![pkg("x", "deno.json", None)],
494+
Box::new(runner),
495+
);
496+
497+
a.update_lockfile(tmp.path()).unwrap();
498+
499+
assert!(calls.lock().unwrap().is_empty());
500+
}
501+
393502
#[test]
394503
fn reads_and_writes_nested_json_version_field() {
395504
let tmp = tempfile::tempdir().unwrap();

crates/core/src/init.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,23 @@ fn render_build_job(s: &mut String, entry: &PackageEntry) {
475475

476476
s.push_str(" steps:\n");
477477
s.push_str(" - uses: actions/checkout@v4\n");
478+
if entry.matrix
479+
&& entry
480+
.targets
481+
.iter()
482+
.any(|target| target.name == "linux" && target.arch == "aarch64")
483+
{
484+
s.push_str(" - name: Install Linux aarch64 cross toolchain\n");
485+
s.push_str(" if: matrix.rust_target == 'aarch64-unknown-linux-gnu'\n");
486+
s.push_str(" run: |\n");
487+
s.push_str(" sudo apt-get update\n");
488+
s.push_str(" sudo apt-get install -y gcc-aarch64-linux-gnu\n");
489+
s.push_str(" - name: Configure Linux aarch64 linker\n");
490+
s.push_str(" if: matrix.rust_target == 'aarch64-unknown-linux-gnu'\n");
491+
s.push_str(
492+
" run: echo \"CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc\" >> \"$GITHUB_ENV\"\n",
493+
);
494+
}
478495
match entry.adapter {
479496
Ecosystem::Cargo => {
480497
s.push_str(" - uses: dtolnay/rust-toolchain@stable\n");

docs/adapters/generic.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ a release PR, and a publish/release workflow scaffold. Implemented in
1818
package is `publish` mode and ships through `otf-release publish`, which runs your command and
1919
then tags + creates the GitHub Release. When omitted, the package is `build-only`.
2020
- **Build** is optional — `command` + `artifacts`, like any other adapter, for staging files.
21-
- **No dependency graph, lockfile, or ranges** — those trait methods are no-ops.
21+
- **No dependency graph or ranges** — those trait methods are no-ops.
22+
- **Lockfile** is normally a no-op. If a generic package versions a root `Cargo.toml` and
23+
`Cargo.lock` exists, `otf-release version` runs `cargo update --workspace` so the lockfile is
24+
refreshed in the release commit.
2225

2326
## `release.toml`
2427

0 commit comments

Comments
 (0)