This repository implements a simple Rust command-line interface application that converts webpages to markdown files.
This repository is a GitHub Template that you can use to create a new repository for Rust-based command-line applications. It comes pre-configured to use cargo 1.76.0
with automated setup for Ubuntu 20.04/22.04
.
To get started, you can:
-
Simply click
Use this template
in the top right corner of this repository, or -
Clone this repository onto your machine with:
git clone https://github.com/christopherkeim/rust-cli-template.git
Note: this template targets Ubuntu 20.04/22.04 development environments for automated setup.
- Once you have local copy of this repository in your development environment, navigate into this directory and run the
setup.sh
script:
cd rust-cli-template/
bash setup.sh
If you would like to include Docker 24.0.6
in your development environment, you can run:
bash setup.sh --docker
- You can ensure that the code currently passes the provided tests with:
make test
- You can directly try the CLI application with:
cargo run -- \
--url https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/first-edition/error-handling.html \
--file rust_error_handling.md
This webpage is an excellent resource on error handling rust 😊.
I like to use a Makefile
to automate parts of my development workflow - this allows you to alias parts of your development process like linting, formatting, testing, and building binaries.
The Makefile
in this repository comes with the following commands:
make build
cargo build --release
make clean
cargo clean
make doc
cargo doc --no-deps --open
make lint
cargo clippy
make format
cargo fmt
make test
cargo test
This repository has continuous integration that runs when Pull Requests are opened to the main
branch.
Note that GitHub Action runners come pre-installed with cargo 1.76.0 (latest)
The continuous integration pipeline implemented in ci.yaml
runs the following steps:
-
Checkout
: Checks out the source code into the runner VM -
Format
: Formats the source code files -
Test
: Runs the unit tests defined in each module -
Build
: Builds a release binary for deployment (commented out - replace with your deployment strategy)
This template includes a Dockerfile
that implements a multistage build with the final image using scratch
and the rust_cli_template
release binary as its entrypoint.
The
setup.sh
script is idempotent, and it will only make changes that bring your development environment closer to the desired state described in the script. If you'd like to includedocker
in your development environment, you can run it again with--docker
.
If you'd like to containerize the application:
- Create the image:
docker build -t rust-cli-template:v0 .
- Run the container with:
docker run -it rust-cli-template:v0 \
--url https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/first-edition/error-handling.html \
--file rust_error_handling.md
For this example application, it's a bit silly. But I've included it as a convenient starting point for building cloud native workloads.
The rust:1.76-alpine
official Rust image uses musl libc
to a build statically-linked Rust release binary. This is crucial for allowing the binary to run in our scratch
image.
FROM scratch
essentially is an empty container - it is a no-op in the docker build
process and it is used as a starting point for building minimal Docker Images ground up.
https://hub.docker.com/_/scratch:
You can use Docker’s reserved, minimal image,
scratch
, as a starting point for building containers. Using thescratch
“image” signals to the build process that you want the next command in the Dockerfile to be the first filesystem layer in your image.While scratch appears in Docker’s repository on the hub, you can’t pull it, run it, or tag any image with the name scratch. Instead, you can refer to it in your
Dockerfile
.
In addition to making making your final image's size extremely small, it also hardens the security posture of our software by isolating the execution environment of the program - if an attacker penetrates into your runtime environment, it's an empty container with no further exploitable software.
Cargo has two main profiles: the dev
profile Cargo uses when you run cargo build
and the release
profile Cargo uses when you run cargo build --release
. The dev profile is defined with good defaults for development, and the release profile has good defaults for release builds.
These profile names might be familiar from the output of your builds:
$ cargo build
Finished dev [unoptimized + debuginfo] target(s) in 0.0s
$ cargo build --release
Finished release [optimized] target(s) in 0.0s
The dev and release are these different profiles used by the compiler.
By adding [profile.*]
sections for any profile you want to customize, you override any subset of the default settings. For example, here are the default values for the opt-level
setting for the dev
and release
profiles:
Filename: Cargo.toml
[profile.dev]
opt-level = 0
[profile.release]
opt-level = 3
The opt-level
setting controls the number of optimizations Rust will apply to your code, with a range of 0
to 3
. Applying more optimizations extends compiling time, so if you’re in development and compiling your code often, you’ll want fewer optimizations to compile faster even if the resulting code runs slower.
The best documentation with very clear examples is Rust By Example: https://doc.rust-lang.org/rust-by-example/index.html
rustc
is a cross-compiler by default. This means that you can use any compiler to build for any architecture. The list of targets are the possible architectures that you can build for can be seen with:
rustc --print target-list
The rustc Book: https://doc.rust-lang.org/rustc/targets/index.html
https://jakewharton.com/cross-compiling-static-rust-binaries-in-docker-for-raspberry-pi/
Documentation on Rust's package manager cargo
.