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 templatein the top right corner of this repository, or -
Clone this repository onto your machine with:
git clone https://github.com/christopherkeim/rust-cli-template.gitNote: 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.shscript:
cd rust-cli-template/
bash setup.shIf 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.mdThis 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 --releasemake clean
cargo cleanmake doc
cargo doc --no-deps --openmake lint
cargo clippymake format
cargo fmtmake test
cargo testThis 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.shscript 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 includedockerin 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.mdFor 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 = 3The 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-listThe 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.