Skip to content

Commit

Permalink
feat: first iteration ready
Browse files Browse the repository at this point in the history
- Generate `docker run` command
- Add README
- Add Dockerfile
- add CI for release
- >70% faster than `runlike`
  • Loading branch information
ByteBaker committed Sep 14, 2024
1 parent cf26eb4 commit ce1e4da
Show file tree
Hide file tree
Showing 11 changed files with 597 additions and 0 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Build Project

on:
push:
branches:
- main

jobs:
build:
runs-on: ${{ matrix.platform }}

strategy:
matrix:
platform: [ubuntu-latest]

steps:
# Checkout the repository
- name: Checkout code
uses: actions/checkout@v3

# Install Rust
- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true

# Build the project
- name: Build the project
run: cargo build --release

# Upload the binary as an artifact, correctly naming it based on platform
- name: Upload binary artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.platform }}-binary
path: |
target/release/runlike${{ matrix.platform == 'windows-latest' && '.exe' || '' }}
147 changes: 147 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
name: Release Artifacts

on:
push:
branches:
- release

jobs:
build:
runs-on: ${{ matrix.config.os }}

strategy:
matrix:
config:
- os: ubuntu-latest
tag: linux-amd64
is_version_job: true # Designate one job for versioning
- os: windows-latest
tag: windows-amd64
is_version_job: false
- os: macos-latest
tag: macos-amd64
is_version_job: false

steps:
# Checkout the repository
- name: Checkout code
uses: actions/checkout@v3

# Install Rust
- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true

# Build the project
- name: Build the project
run: cargo build --release

# Get the version from Cargo.toml
- name: Get version from Cargo.toml
id: get_version
if: matrix.config.is_version_job == true
run: |
# Extract the version from Cargo.toml
version=$(cargo pkgid | cut -d "@" -f2)
echo "VERSION=$version" >> $GITHUB_ENV
echo "$version" > version.txt
# Upload the binary as an artifact, correctly naming it based on platform
- name: Upload binary artifact
uses: actions/upload-artifact@v4
with:
name: runlike-${{ matrix.config.tag }}
path: |
target/release/runlike${{ matrix.config.os == 'windows-latest' && '.exe' || '' }}
- name: Upload version file
if: matrix.config.is_version_job == true
uses: actions/upload-artifact@v4
with:
name: project-version
path: version.txt

release:
runs-on: ubuntu-latest
needs: build

steps:
- name: Checkout code
uses: actions/checkout@v4

# Download the build artifacts from the build job
- name: Download Linux binary
uses: actions/download-artifact@v4
with:
name: runlike-linux-amd64

- name: Download MacOS binary
uses: actions/download-artifact@v4
with:
name: runlike-macos-amd64

- name: Download Windows binary
uses: actions/download-artifact@v4
with:
name: runlike-windows-amd64

- name: Download version file
uses: actions/download-artifact@v4
with:
name: project-version

# Read the version from the version file
- name: Read version
id: read_version
run: |
VERSION=$(cat version.txt)
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "Version is $VERSION"
# Create a new GitHub release and upload binaries
- name: Create GitHub Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ env.VERSION }}
release_name: "v${{ env.VERSION }}"
draft: false
prerelease: false

# Upload the compiled binaries to the created release
- name: Upload Linux binary to release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: actions/upload-release-asset@v1
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: runlike
asset_name: runlike-linux-amd64
asset_content_type: application/octet-stream

# Upload the compiled binaries to the created release
- name: Upload MacOS binary to release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: actions/upload-release-asset@v1
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: runlike
asset_name: runlike-macos-amd64
asset_content_type: application/octet-stream

# Upload the compiled binaries to the created release
- name: Upload Windows binary to release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: actions/upload-release-asset@v1
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: runlike.exe
asset_name: runlike-windows-amd64.exe
asset_content_type: application/octet-stream
33 changes: 33 additions & 0 deletions .github/workflows/rust-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Rust CI

on:
push:
branches:
- master
pull_request:
branches:
- master

jobs:
build-and-test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
toolchain: [stable]

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Build
run: cargo build --verbose

- name: Format
run: cargo fmt --all -- --check

- name: Clippy
run: cargo clippy

- name: Run tests
run: cargo test --verbose
18 changes: 18 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "runlike"
version = "0.1.0"
edition = "2021"

[profile.release]
strip = true
lto = true

[dependencies]
clap = { version = "4.5.17", default-features = false, features = [
"std",
"derive",
"help",
] }
itertools = { version = "0.13", default-features = false }
serde = { version = "1.0", default-features = false, features = ["derive"] }
serde_json = { version = "1.0", default-features = false, features = ["std"] }
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM rust:latest AS builder

# Alipne uses musl instead of glibc
RUN rustup target add x86_64-unknown-linux-musl

WORKDIR /app
COPY Cargo.toml .
COPY src ./src

RUN cargo build --release --target x86_64-unknown-linux-musl

FROM alpine:latest
RUN apk add --no-cache docker-cli

COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/runlike /usr/local/bin/runlike

ENTRYPOINT ["/usr/local/bin/runlike"]
75 changes: 75 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#### Runlike, (re)written in Rust

"See this docker container? I wish I could run another one just like it,
but I'll be damned if I'm going to type all those command-line switches manually!"

This is what `runlike` does. You give it a docker container, it outputs the command line necessary to run another one just like it, including the arguments that went while creating it. It's a real time saver for those that normally deploy their docker containers via some CM tool like Ansible/Chef and then find themselves needing to manually re-run some container.

# Usage
runlike <container-name>
This prints out what you need to run to get a similar container. You can do `$(runlike container-name)` to simply execute its output in one step.

`-p` breaks the command line down to nice, pretty lines. For example:

$ runlike -p redis

docker run \
--name=redis \
-e "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
-e "REDIS_VERSION=2.8.9" \
-e "REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-2.8.9.tar.gz" \
-e "REDIS_DOWNLOAD_SHA1=003ccdc175816e0a751919cf508f1318e54aac1e" \
-p 0.0.0.0:6379:6379/tcp \
--detach=true \
myrepo/redis:7860c450dbee9878d5215595b390b9be8fa94c89 \
redis-server --slaveof 172.31.17.84 6379

Feeding it the output of `docker inspect` also works:

```
docker inspect <container-name> | runlike --stdin
```

`--no-name` will omit the container name from the output (to avoid collisions).


# Status

This is very much a work in progress. Many `docker run` options aren't yet supported, but the most commonly used ones are. Feel free to send pull requests if you add any or if you happen to fix any (of the many) bugs this package undoubtedly has.

Probably **shouldn't use this in production** yet. If you do, double check that it's actually running what you want it to run.

## Supported Run Options

```
--cpuset-cpus string CPUs in which to allow execution
(0-3, 0,1)
--cpuset-mems string MEMs in which to allow execution
-d, --detach Run container in background and
print container ID
-h, --hostname string Container host name
--mac-address string Container MAC address (e.g.,
92:d0:c6:0a:29:33)
-m, --memory bytes Memory limit
--name string Assign a name to the container
--network string Connect a container to a network
(default "default")
--pid string PID namespace to use
--privileged Give extended privileges to this
container
--runtime string Runtime to use for this container
-u, --user string Username or UID (format:
<name|uid>[:<group|gid>])
```

## Not Yet Supported Run Options (PRs are most welcome!)
Pretty much everything else. But work is in progress.

## Roadmap:
- [ ] Support more flags
- [ ] Installation instructions
- [ ] Setup CI and release binaries
- [ ] Run without installation

## Footnote
The work here is based on [Assaf Lavie](https://assaf.io)'s [runlike](https://github.com/lavie/runlike). I was lazy so the README is mostly a ripoff too. It's still a work in progress, and most features aren't yet supported. But we'll get there, sooner than later.
20 changes: 20 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use clap::Parser;

#[derive(Debug, Parser)]
#[command(
version,
about,
long_about = "This is a Rust implementation of `runlike` written in Python"
)]
pub(super) struct CliArgs {
/// The container name or ID
pub container: Option<String>,
/// Break down the output into multiple lines
#[arg(short, long)]
pub pretty: bool,
#[arg(short, long)]
pub stdin: bool,
/// Do not include container name in output
#[arg(long)]
pub no_name: bool,
}
18 changes: 18 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
mod args;
mod model;
mod options;
mod util;

use options::Options;
use std::convert::Infallible;

fn main() -> Result<(), Infallible> {
match Options::try_new() {
Ok(options) => {
options.print();
}
Err(e) => eprintln!("Error: {e}"),
}

Ok(())
}
Loading

0 comments on commit ce1e4da

Please sign in to comment.