Compare commits
9 Commits
45f2bb64b0
...
8a35904c79
Author | SHA1 | Date | |
---|---|---|---|
|
8a35904c79 | ||
|
c55ccf005b | ||
|
b6a234a833 | ||
|
2a0dee78a7 | ||
|
b67242d0ea | ||
|
7b8e613058 | ||
|
74006af796 | ||
|
d608fe6b50 | ||
|
3cfbc2a656 |
37
.woodpecker.yml
Normal file
37
.woodpecker.yml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# https://woodpecker-ci.org/docs/usage/intro
|
||||||
|
pipeline:
|
||||||
|
|
||||||
|
build_and_test:
|
||||||
|
image: rust
|
||||||
|
commands:
|
||||||
|
- cargo test
|
||||||
|
- cargo build --release
|
||||||
|
|
||||||
|
gitea_on_release:
|
||||||
|
# http://plugins.drone.io/drone-plugins/drone-gitea-release/
|
||||||
|
image: plugins/gitea-release
|
||||||
|
files: target/release/reel-moby
|
||||||
|
secrets: [gitea_release_api_key, gitea_release_base_url]
|
||||||
|
when:
|
||||||
|
event: tag
|
||||||
|
tag: v*
|
||||||
|
|
||||||
|
github_on_release:
|
||||||
|
# http://plugins.drone.io/drone-plugins/drone-github-release/
|
||||||
|
image: plugins/github-release
|
||||||
|
files: target/release/reel-moby
|
||||||
|
secrets: [github_release_api_key]
|
||||||
|
when:
|
||||||
|
event: tag
|
||||||
|
tag: v*
|
||||||
|
|
||||||
|
notify_when_failure:
|
||||||
|
# http://plugins.drone.io/appleboy/drone-discord/
|
||||||
|
image: appleboy/drone-discord
|
||||||
|
secrets: [ discord_webhook_id, discord_webhook_token]
|
||||||
|
message: "build {{build.number}} or release failed. Fix me please."
|
||||||
|
when:
|
||||||
|
status: failure
|
||||||
|
|
||||||
|
|
||||||
|
# http://plugins.drone.io/drone-plugins/drone-github-release/
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "reel-moby"
|
name = "reel-moby"
|
||||||
version = "0.10.0"
|
version = "0.11.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
@ -7,4 +7,4 @@ search for new docker tags and update them in your docker-compose file
|
|||||||
Searches the current folder for a docker-compose.(yml|yaml) file and opens it when it found one. Then it is possible to select a image line. The program then shows the found repository and shows the latest tags. The tags can be scrolled and selected, which updates the opened file.
|
Searches the current folder for a docker-compose.(yml|yaml) file and opens it when it found one. Then it is possible to select a image line. The program then shows the found repository and shows the latest tags. The tags can be scrolled and selected, which updates the opened file.
|
||||||
From that point save the file and pull the new image with `docker-compose up -d` or `docker-compse pull`.
|
From that point save the file and pull the new image with `docker-compose up -d` or `docker-compse pull`.
|
||||||
|
|
||||||
[screenshot](./screenshot.png)
|
![screenshot](./screenshot.png)
|
||||||
|
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 41 KiB |
@ -15,7 +15,7 @@ pub struct Opt {
|
|||||||
|
|
||||||
/// A custom path to a docker-compose file
|
/// A custom path to a docker-compose file
|
||||||
#[structopt(short, long, parse(from_os_str))]
|
#[structopt(short, long, parse(from_os_str))]
|
||||||
config: Option<PathBuf>,
|
file: Option<PathBuf>,
|
||||||
|
|
||||||
/// Give a Repository identifier, e.g. library/nginx
|
/// Give a Repository identifier, e.g. library/nginx
|
||||||
#[structopt(short, long, parse(from_str))]
|
#[structopt(short, long, parse(from_str))]
|
||||||
|
@ -2,24 +2,16 @@ use std::fmt;
|
|||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
// use crate::common;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
// Conversion,
|
|
||||||
// Empty,
|
|
||||||
NoTagFound,
|
NoTagFound,
|
||||||
// InvalidChar,
|
|
||||||
MisformedInput,
|
MisformedInput,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
// Error::Conversion => write!(f, "Conversion error"),
|
|
||||||
// Error::Empty => write!(f, "Input is empty"),
|
|
||||||
Error::NoTagFound => write!(f, "Expected a tag"),
|
Error::NoTagFound => write!(f, "Expected a tag"),
|
||||||
// Error::InvalidChar => write!(f, "Invalid character found"),
|
|
||||||
Error::MisformedInput => write!(f, "Unexpected input"),
|
Error::MisformedInput => write!(f, "Unexpected input"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
60
src/tags.rs
60
src/tags.rs
@ -7,7 +7,6 @@ use serde::Deserialize;
|
|||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
struct ImageDetails {
|
struct ImageDetails {
|
||||||
architecture: String,
|
architecture: String,
|
||||||
// os: String,
|
|
||||||
size: usize,
|
size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,6 +24,30 @@ pub struct Images {
|
|||||||
last_updated: String,
|
last_updated: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Images {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
//architecture infos
|
||||||
|
let mut arch = String::new();
|
||||||
|
for image in self.images.iter().take(1) {
|
||||||
|
arch.push_str(&format!("{}", image));
|
||||||
|
}
|
||||||
|
for image in self.images.iter().skip(1) {
|
||||||
|
arch.push_str(&format!(", {}", image));
|
||||||
|
}
|
||||||
|
|
||||||
|
let now = chrono::Utc::now();
|
||||||
|
let rfc3339 = DateTime::parse_from_rfc3339(&self.last_updated).unwrap();
|
||||||
|
let dif = now - rfc3339.with_timezone(&chrono::Utc);
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{} vor {} [{}]",
|
||||||
|
self.tag_name,
|
||||||
|
format_time_nice(dif),
|
||||||
|
arch
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Tags {
|
pub struct Tags {
|
||||||
count: usize,
|
count: usize,
|
||||||
@ -41,7 +64,6 @@ pub enum Error {
|
|||||||
Converting(String),
|
Converting(String),
|
||||||
/// invalid repos show a valid json with 0 tags
|
/// invalid repos show a valid json with 0 tags
|
||||||
NoTagsFound,
|
NoTagsFound,
|
||||||
NoNextPage,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
@ -49,7 +71,6 @@ impl fmt::Display for Error {
|
|||||||
match self {
|
match self {
|
||||||
Error::Fetching(s) => write!(f, "Fetching error: {}", s),
|
Error::Fetching(s) => write!(f, "Fetching error: {}", s),
|
||||||
Error::Converting(s) => write!(f, "Converting error: {}", s),
|
Error::Converting(s) => write!(f, "Converting error: {}", s),
|
||||||
Error::NoNextPage => write!(f, "No next page available"),
|
|
||||||
Error::NoTagsFound => write!(f, "Given Repo has 0 tags. Is it valid?"),
|
Error::NoTagsFound => write!(f, "Given Repo has 0 tags. Is it valid?"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,38 +119,17 @@ impl Tags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// returns tags of next page
|
/// returns tags of next page
|
||||||
pub fn next_page(&self) -> Result<Self, Error> {
|
pub fn next_page(&self) -> Option<Self> {
|
||||||
match &self.next_page {
|
match &self.next_page {
|
||||||
Some(url) => Self::with_url(url),
|
Some(url) => match Self::with_url(url) {
|
||||||
None => Err(Error::NoNextPage),
|
Ok(tags) => Some(tags),
|
||||||
|
Err(_) => None,
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Images {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
//architecture infos
|
|
||||||
let mut arch = String::new();
|
|
||||||
for image in self.images.iter().take(1) {
|
|
||||||
arch.push_str(&format!("{}", image));
|
|
||||||
}
|
|
||||||
for image in self.images.iter().skip(1) {
|
|
||||||
arch.push_str(&format!(", {}", image));
|
|
||||||
}
|
|
||||||
|
|
||||||
let now = chrono::Utc::now();
|
|
||||||
let rfc3339 = DateTime::parse_from_rfc3339(&self.last_updated).unwrap();
|
|
||||||
let dif = now - rfc3339.with_timezone(&chrono::Utc);
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{} vor {} [{}]",
|
|
||||||
self.tag_name,
|
|
||||||
format_time_nice(dif),
|
|
||||||
arch
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// converts a given duration to a readable string
|
/// converts a given duration to a readable string
|
||||||
fn format_time_nice(time: chrono::Duration) -> String {
|
fn format_time_nice(time: chrono::Duration) -> String {
|
||||||
if time.num_weeks() == 52 {
|
if time.num_weeks() == 52 {
|
||||||
|
@ -54,7 +54,7 @@ impl Ui {
|
|||||||
state: State::SelectService,
|
state: State::SelectService,
|
||||||
repo: repo_entry::RepoEntry::new(repo_id),
|
repo: repo_entry::RepoEntry::new(repo_id),
|
||||||
tags: tag_list::TagList::with_status("Tags are empty"),
|
tags: tag_list::TagList::with_status("Tags are empty"),
|
||||||
services: service_switcher::ServiceSwitcher::new(&opt.config).unwrap(),
|
services: service_switcher::ServiceSwitcher::new(&opt.file).unwrap(),
|
||||||
info: info::Info::new("Select image of edit Repository"),
|
info: info::Info::new("Select image of edit Repository"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -122,6 +122,7 @@ impl Ui {
|
|||||||
State::SelectTag => {
|
State::SelectTag => {
|
||||||
let mut repo = ui.repo.get();
|
let mut repo = ui.repo.get();
|
||||||
let tag = match ui.tags.get_selected() {
|
let tag = match ui.tags.get_selected() {
|
||||||
|
Err(tag_list::Error::NextPageSelected) => continue,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
ui.info.set_info(&format!("{}", e));
|
ui.info.set_info(&format!("{}", e));
|
||||||
continue;
|
continue;
|
||||||
|
@ -10,7 +10,7 @@ use termion::input::TermRead;
|
|||||||
use crate::widget::service_switcher;
|
use crate::widget::service_switcher;
|
||||||
|
|
||||||
pub fn create_ui(opt: &Opt) {
|
pub fn create_ui(opt: &Opt) {
|
||||||
let service_result = service_switcher::ServiceSwitcher::new(&opt.config);
|
let service_result = service_switcher::ServiceSwitcher::new(&opt.file);
|
||||||
match service_result {
|
match service_result {
|
||||||
None => no_yaml::NoYaml::run(opt),
|
None => no_yaml::NoYaml::run(opt),
|
||||||
Some(_) => default::Ui::run(opt),
|
Some(_) => default::Ui::run(opt),
|
||||||
|
@ -11,7 +11,7 @@ impl Info {
|
|||||||
Self {
|
Self {
|
||||||
info: String::from(info),
|
info: String::from(info),
|
||||||
keys: String::from(
|
keys: String::from(
|
||||||
"Tab Cycle widgets C-s Save C-r Reload C-q Quit ↑ ↓ Select tags or image line",
|
"Tab Cycle widgets C-s Save C-r Reload C-q Quit ↑ ↓ Select tags or image line Return Select current selection",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,8 +68,8 @@ impl TagList {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
match tags.next_page() {
|
match tags.next_page() {
|
||||||
Err(_) => (),
|
None => (),
|
||||||
Ok(new_tags) => {
|
Some(new_tags) => {
|
||||||
lines.push(Line::NextPage(String::from("load more tags")));
|
lines.push(Line::NextPage(String::from("load more tags")));
|
||||||
tags = new_tags;
|
tags = new_tags;
|
||||||
}
|
}
|
||||||
@ -149,8 +149,8 @@ impl TagList {
|
|||||||
fn load_next_page(&mut self) {
|
fn load_next_page(&mut self) {
|
||||||
match &self.tags {
|
match &self.tags {
|
||||||
Some(tags) => match tags.next_page() {
|
Some(tags) => match tags.next_page() {
|
||||||
Err(_) => (),
|
None => (),
|
||||||
Ok(new_tags) => {
|
Some(new_tags) => {
|
||||||
//load new tags object
|
//load new tags object
|
||||||
self.tags = Some(new_tags);
|
self.tags = Some(new_tags);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user