m/cup
1
0
mirror of https://github.com/sergi0g/cup.git synced 2025-11-09 21:53:50 -05:00

15 Commits

Author SHA1 Message Date
Sergio
c969ded188 Update version 2024-08-31 19:54:51 +03:00
Sergio
53f32958fc Update build.yml
Attempt "I've lost count"
2024-08-31 19:36:56 +03:00
Sergio
82ec9b6e52 Update build.yml
Attempt 5. I just hate the fact that you can't properly debug this thing.
2024-08-31 19:22:28 +03:00
Sergio
8ad5cbb127 Update build.yml
Attempt 4
2024-08-31 19:16:59 +03:00
Sergio
7ea4c63322 Update build.yml
Attempt 3
2024-08-31 19:10:52 +03:00
Sergio
30b8e943c0 Update build.yml
(Hopefully) fixed CI errors
2024-08-31 19:07:19 +03:00
Sergio
0f7245dbf4 Update build.yml
Test
2024-08-31 18:54:52 +03:00
Sergio
2549ed7801 Add support for private registries/images 2024-08-31 18:43:30 +03:00
Sergio
90239f83e9 New version
Some checks are pending
Nightly Release / build-binary (map[bin:cup command:build name:cup-linux-aarch64 os:ubuntu-latest release_for:linux-aarch64 target:aarch64-unknown-linux-musl]) (push) Waiting to run
Nightly Release / build-binary (map[bin:cup command:build name:cup-linux-x86_64 os:ubuntu-latest release_for:linux-x86_64 target:x86_64-unknown-linux-musl]) (push) Waiting to run
Nightly Release / build-image (push) Waiting to run
2024-08-30 19:30:28 +03:00
Sergio
dc7a981930 Merge pull request #22 from sergi0g/set-correct-headers
Set correct headers when requesting a manifest to prevent getting a v1 manifest
2024-08-30 19:19:11 +03:00
Sergio
8d2740dc7d Set correct headers when requesting a manifest to prevent getting a v1 manifest 2024-08-30 19:17:43 +03:00
Sergio
fb674acf96 Update gitignore
Some checks are pending
Nightly Release / build-binary (map[bin:cup command:build name:cup-linux-aarch64 os:ubuntu-latest release_for:linux-aarch64 target:aarch64-unknown-linux-musl]) (push) Waiting to run
Nightly Release / build-binary (map[bin:cup command:build name:cup-linux-x86_64 os:ubuntu-latest release_for:linux-x86_64 target:x86_64-unknown-linux-musl]) (push) Waiting to run
Nightly Release / build-image (push) Waiting to run
2024-08-30 11:32:33 +03:00
Sergio
e9160334d9 Update README.md
Some checks failed
Nightly Release / build-binary (map[bin:cup command:build name:cup-linux-aarch64 os:ubuntu-latest release_for:linux-aarch64 target:aarch64-unknown-linux-musl]) (push) Has been cancelled
Nightly Release / build-binary (map[bin:cup command:build name:cup-linux-x86_64 os:ubuntu-latest release_for:linux-x86_64 target:x86_64-unknown-linux-musl]) (push) Has been cancelled
Nightly Release / build-image (push) Has been cancelled
Fix broken docs link
2024-08-21 16:23:28 +03:00
Sergio
ca6ffea29c Fix sort order in docs and a typo
Some checks failed
Deploy github pages / build (push) Has been cancelled
Nightly Release / build-binary (map[bin:cup command:build name:cup-linux-aarch64 os:ubuntu-latest release_for:linux-aarch64 target:aarch64-unknown-linux-musl]) (push) Has been cancelled
Nightly Release / build-binary (map[bin:cup command:build name:cup-linux-x86_64 os:ubuntu-latest release_for:linux-x86_64 target:x86_64-unknown-linux-musl]) (push) Has been cancelled
Nightly Release / build-image (push) Has been cancelled
Deploy github pages / deploy (push) Has been cancelled
2024-07-17 17:28:05 +03:00
Sergio
923e81d75d Update release workflow and fix bug with binaries not being uploaded
Some checks are pending
Deploy github pages / build (push) Waiting to run
Deploy github pages / deploy (push) Blocked by required conditions
Nightly Release / build-binary (map[bin:cup command:build name:cup-linux-aarch64 os:ubuntu-latest release_for:linux-aarch64 target:aarch64-unknown-linux-musl]) (push) Waiting to run
Nightly Release / build-binary (map[bin:cup command:build name:cup-linux-x86_64 os:ubuntu-latest release_for:linux-x86_64 target:x86_64-unknown-linux-musl]) (push) Waiting to run
Nightly Release / build-image (push) Waiting to run
2024-07-17 16:15:26 +03:00
16 changed files with 174 additions and 113 deletions

View File

@@ -18,21 +18,10 @@ jobs:
build-binary: build-binary:
strategy: strategy:
matrix: matrix:
platform: arch:
- release_for: linux-aarch64 - aarch64
os: ubuntu-latest - x86_64
target: aarch64-unknown-linux-musl runs-on: ubuntu-latest
bin: cup
name: cup-linux-aarch64
command: build
- release_for: linux-x86_64
os: ubuntu-latest
target: x86_64-unknown-linux-musl
bin: cup
name: cup-linux-x86_64
command: build
runs-on: ${{ matrix.platform.os }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -44,13 +33,13 @@ jobs:
run: cargo install cross --git https://github.com/cross-rs/cross run: cargo install cross --git https://github.com/cross-rs/cross
- name: Build binary - name: Build binary
run: cross ${{ matrix.platform.command }} --target ${{ matrix.platform.target }} --release run: cross build --target ${{ matrix.arch }}-unknown-linux-musl --release
- name: Upload binary - name: Upload binary
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: ${{ matrix.platform.name }} name: cup-linux-${{ matrix.arch }}
path: target/${{ matrix.platform.target }}/release/${{ matrix.platform.bin }} path: target/${{ matrix.arch }}-unknown-linux-musl/release/cup
build-image: build-image:
needs: get-tag needs: get-tag
@@ -98,6 +87,11 @@ jobs:
name: cup-linux-x86_64 name: cup-linux-x86_64
path: cup-linux-x86_64 path: cup-linux-x86_64
# - name: Extract and rename binaries
# run: |
# unzip /home/runner/work/cup/cup/cup-linux-aarch64 && mv /home/runner/work/cup/cup/cup cup-linux-aarch64
# unzip /home/runner/work/cup/cup/cup-linux-x86_64 && mv /home/runner/work/cup/cup/cup cup-linux-x86_64
- name: Create release - name: Create release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
env: env:
@@ -107,5 +101,5 @@ jobs:
tag_name: ${{ needs.get-tag.outputs.tag }} tag_name: ${{ needs.get-tag.outputs.tag }}
name: ${{ needs.get-tag.outputs.tag }} name: ${{ needs.get-tag.outputs.tag }}
files: | files: |
cup-linux-aarch64/cup-linux-aarch64 cup-linux-aarch64
cup-linux-x86_64/cup-linux-x86_64 cup-linux-x86_64

5
.gitignore vendored
View File

@@ -1,4 +1,7 @@
/target /target
/docs/.next /docs/.next
/docs/node_modules /docs/node_modules
/docs/out /docs/out
# In case I accidentally commit mine...
cup.json

2
Cargo.lock generated
View File

@@ -350,7 +350,7 @@ dependencies = [
[[package]] [[package]]
name = "cup" name = "cup"
version = "1.1.3" version = "2.1.0"
dependencies = [ dependencies = [
"bollard", "bollard",
"chrono", "chrono",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "cup" name = "cup"
version = "1.1.3" version = "2.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@@ -20,7 +20,7 @@ Cup is the easiest way to check for container image updates.
## Documentation ## Documentation
Take a look at https://sergi0g.github.io/cup/docs/introduction! Take a look at https://sergi0g.github.io/cup/docs!
## Limitations ## Limitations

1
docs/.tool-versions Normal file
View File

@@ -0,0 +1 @@
nodejs 21.6.2

View File

@@ -2,6 +2,15 @@
"index": { "index": {
"title": "Introduction" "title": "Introduction"
}, },
"installation": {
"title": "Installation"
},
"configuration": {
"title": "Configuration"
},
"usage": {
"title": "Usage"
},
"nightly": { "nightly": {
"title": "Using the latest version" "title": "Using the latest version"
} }

View File

@@ -1,7 +1,5 @@
import Image from "next/image"; import { Steps, Callout, Card, Cards } from "nextra-theme-docs";
import { Steps, Callout } from "nextra-theme-docs"; import { IconPaint, IconLockOpen, IconKey } from '@tabler/icons-react';
import blue from "../../assets/blue_theme.png"
import gray from "../../assets/gray_theme.png"
# Configuration # Configuration
@@ -15,6 +13,8 @@ For example, if using Podman, you might do
$ cup -s /run/user/1000/podman/podman.sock check $ cup -s /run/user/1000/podman/podman.sock check
``` ```
This option will hopefully be moved to the configuration file soon.
## Configuration file ## Configuration file
Cup has an option to be configured from a configuration file named `cup.json`. Cup has an option to be configured from a configuration file named `cup.json`.
@@ -25,16 +25,23 @@ Create a `cup.json` file somewhere on your system. For binary installs, a path l
If you're running with Docker, you can create a `cup.json` in the directory you're running cup and mount it into the container. _In the next section you will need to use the path where you **mounted** the file_ If you're running with Docker, you can create a `cup.json` in the directory you're running cup and mount it into the container. _In the next section you will need to use the path where you **mounted** the file_
### Configure Cup from the configuration file ### Configure Cup from the configuration file
Follow the guides below (Theme and Authentication) to make your `cup.json` Follow the guides below to customize your `cup.json`
<Cards>
<Card icon={<IconKey />} title="Authentication" href="/docs/configuration/authentication" />
<Card icon={<IconLockOpen />} title="Insecure registries" href="/docs/configuration/insecure-registries" />
<Card icon={<IconPaint />} title="Theme" href="/docs/configuration/theme" />
</Cards>
Here's a full example: Here's a full example:
```json ```json
{ {
authentication: { "authentication": {
"ghcr.io": "<YOUR_TOKEN_HERE>", "ghcr.io": "<YOUR_TOKEN_HERE>",
"registry-1.docker.io": "<YOUR_TOKEN_HERE>" "registry-1.docker.io": "<YOUR_TOKEN_HERE>"
}, },
theme: "blue" "theme": "blue",
"insecure_registries": ["localhost:5000", "my-insecure-registry.example.com"]
} }
``` ```
@@ -48,51 +55,4 @@ $ cup -c /home/sergio/.config/cup.json check
```bash ```bash
$ docker run -tv /var/run/docker.sock:/var/run/docker.sock -v /home/sergio/.config/cup.json:/config/cup.json ghcr.io/sergi0g/cup -c /config/cup.json serve $ docker run -tv /var/run/docker.sock:/var/run/docker.sock -v /home/sergio/.config/cup.json:/config/cup.json ghcr.io/sergi0g/cup -c /config/cup.json serve
``` ```
</Steps> </Steps>
## Theme (server only)
Cup initially had a blue theme which looked like this:
<Image alt="Screenshot of blue theme" src={blue} />
This was replaced by a more neutral theme which is now the default:
<Image alt="Screenshot of neutral theme" src={gray} />
However, you can get the old theme back by adding the `theme` key to your `cup.json`
Available values are `default` and `blue`.
Here's an example:
```json
{
"theme": "blue",
// Other options
}
```
## Authentication
<Callout emoji="⛔">
The features described in this section have not been implemented yet.
</Callout>
Some registries (or specific images) may require you to be authenticated. For those, you can modify `cup.json` like this:
```json
{
"authentication": {
"<YOUR_REGISTRY_DOMAIN_1>": "<YOUR_TOKEN_1>",
"<YOUR_REGISTRY_DOMAIN_2>": "<YOUR_TOKEN_2>"
// ...
},
// Other options
}
```
You can use any registry, like `ghcr.io`, `quay.io`, `gcr.io`, etc.
<Callout emoji="⚠️">
For Docker Hub, use `registry-1.docker.io`
</Callout>

View File

@@ -0,0 +1,22 @@
import { Callout } from 'nextra-theme-docs'
# Authentication
Some registries (or specific images) may require you to be authenticated. For those, you can modify `cup.json` like this:
```json
{
"authentication": {
"<YOUR_REGISTRY_DOMAIN_1>": "<YOUR_TOKEN_1>",
"<YOUR_REGISTRY_DOMAIN_2>": "<YOUR_TOKEN_2>"
// ...
},
// Other options
}
```
You can use any registry, like `ghcr.io`, `quay.io`, `gcr.io`, etc.
<Callout emoji="⚠️">
For Docker Hub, use `registry-1.docker.io`
</Callout>

View File

@@ -0,0 +1,20 @@
import { Callout } from 'nextra-theme-docs'
# Insecure registries
For the best security, Cup only connects to registries over SSL (HTTPS) by default. However, for people running a local registry that haven't configured SSL, this may be a problem.
To solve this problem, `cup.json` has an `"insecure_registries"` option which allows you to specify exceptions
Here's what it looks like:
```json
{
"insecure_registries": ["<INSECURE_REGISTRY_1>", "<INSECURE_REGISTRY_2>"],
// Other options
}
```
<Callout emoji="⚠️">
When configuring an insecure registry that doesn't run on port 80, don't forget to specify it (i.e. use `localhost:5000` instead of `localhost` if your registry is running on port `5000`)
</Callout>

View File

@@ -0,0 +1,31 @@
import { Callout } from "nextra-theme-docs";
import Image from "next/image";
import blue from "../../../assets/blue_theme.png";
import gray from "../../../assets/gray_theme.png";
# Theme
<Callout emoji="⚠️">
This configuration option is only for the server
</Callout>
Cup initially had a blue theme which looked like this:
<Image alt="Screenshot of blue theme" src={blue} />
This was replaced by a more neutral theme which is now the default:
<Image alt="Screenshot of neutral theme" src={gray} />
However, you can get the old theme back by adding the `theme` key to your `cup.json`
Available values are `default` and `blue`.
Here's an example:
```json
{
"theme": "blue",
// Other options
}
```

View File

@@ -1,8 +1,10 @@
use std::{collections::{HashMap, HashSet}, sync::Mutex}; use std::{collections::{HashMap, HashSet}, sync::Mutex};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use json::JsonValue;
use crate::{docker::get_images_from_docker_daemon, image::Image, registry::{check_auth, get_token, get_latest_digests}, utils::unsplit_image}; use crate::{docker::get_images_from_docker_daemon, image::Image, registry::{check_auth, get_token, get_latest_digests}, utils::unsplit_image};
#[cfg(feature = "cli")] #[cfg(feature = "cli")]
use crate::docker::get_image_from_docker_daemon; use crate::docker::get_image_from_docker_daemon;
#[cfg(feature = "cli")] #[cfg(feature = "cli")]
@@ -23,7 +25,7 @@ where
} }
} }
pub async fn get_all_updates(socket: Option<String>) -> Vec<(String, Option<bool>)> { pub async fn get_all_updates(socket: Option<String>, config: &JsonValue) -> Vec<(String, Option<bool>)> {
let image_map_mutex: Mutex<HashMap<String, &Option<String>>> = Mutex::new(HashMap::new()); let image_map_mutex: Mutex<HashMap<String, &Option<String>>> = Mutex::new(HashMap::new());
let local_images = get_images_from_docker_daemon(socket).await; let local_images = get_images_from_docker_daemon(socket).await;
local_images.par_iter().for_each(|image| { local_images.par_iter().for_each(|image| {
@@ -42,12 +44,13 @@ pub async fn get_all_updates(socket: Option<String>) -> Vec<(String, Option<bool
.par_iter() .par_iter()
.filter(|image| &image.registry == registry) .filter(|image| &image.registry == registry)
.collect(); .collect();
let mut latest_images = match check_auth(registry) { let credentials = config["authentication"][registry].clone().take_string().or(None);
let mut latest_images = match check_auth(registry, config) {
Some(auth_url) => { Some(auth_url) => {
let token = get_token(images.clone(), &auth_url); let token = get_token(images.clone(), &auth_url, &credentials);
get_latest_digests(images, Some(&token)) get_latest_digests(images, Some(&token), config)
} }
None => get_latest_digests(images, None), None => get_latest_digests(images, None, config),
}; };
remote_images.append(&mut latest_images); remote_images.append(&mut latest_images);
} }
@@ -67,15 +70,16 @@ pub async fn get_all_updates(socket: Option<String>) -> Vec<(String, Option<bool
} }
#[cfg(feature = "cli")] #[cfg(feature = "cli")]
pub async fn get_update(image: &str, socket: Option<String>) -> Option<bool> { pub async fn get_update(image: &str, socket: Option<String>, config: &JsonValue) -> Option<bool> {
let local_image = get_image_from_docker_daemon(socket, image).await; let local_image = get_image_from_docker_daemon(socket, image).await;
let token = match check_auth(&local_image.registry) { let credentials = config["authentication"][&local_image.registry].clone().take_string().or(None);
Some(auth_url) => get_token(vec![&local_image], &auth_url), let token = match check_auth(&local_image.registry, config) {
Some(auth_url) => get_token(vec![&local_image], &auth_url, &credentials),
None => String::new(), None => String::new(),
}; };
let remote_image = match token.as_str() { let remote_image = match token.as_str() {
"" => get_latest_digest(&local_image, None), "" => get_latest_digest(&local_image, None, config),
_ => get_latest_digest(&local_image, Some(&token)), _ => get_latest_digest(&local_image, Some(&token), config),
}; };
match &remote_image.digest { match &remote_image.digest {
Some(d) => Some(d != &local_image.digest.unwrap()), Some(d) => Some(d != &local_image.digest.unwrap()),

View File

@@ -69,7 +69,7 @@ async fn main() {
#[cfg(feature = "cli")] #[cfg(feature = "cli")]
Some(Commands::Check { image, icons, raw }) => match image { Some(Commands::Check { image, icons, raw }) => match image {
Some(name) => { Some(name) => {
let has_update = get_update(name, cli.socket).await; let has_update = get_update(name, cli.socket, &config).await;
match raw { match raw {
true => print_raw_update(name, &has_update), true => print_raw_update(name, &has_update),
false => print_update(name, &has_update), false => print_update(name, &has_update),
@@ -77,10 +77,10 @@ async fn main() {
} }
None => { None => {
match raw { match raw {
true => print_raw_updates(&get_all_updates(cli.socket).await), true => print_raw_updates(&get_all_updates(cli.socket, &config).await),
false => { false => {
let spinner = Spinner::new(); let spinner = Spinner::new();
let updates = get_all_updates(cli.socket).await; let updates = get_all_updates(cli.socket, &config).await;
spinner.succeed(); spinner.succeed();
print_updates(&updates, icons); print_updates(&updates, icons);
} }

View File

@@ -6,43 +6,48 @@ use ureq::Error;
use http_auth::parse_challenges; use http_auth::parse_challenges;
use crate::{error, image::Image}; use crate::{error, image::Image, warn};
pub fn check_auth(registry: &str) -> Option<String> { pub fn check_auth(registry: &str, config: &JsonValue) -> Option<String> {
let response = ureq::get(&format!("https://{}/v2/", registry)).call(); let protocol = if config["insecure_registries"].contains(registry) { "http" } else { "https" };
let response = ureq::get(&format!("{}://{}/v2/", protocol, registry)).call();
match response { match response {
Ok(_) => None, Ok(_) => None,
Err(Error::Status(401, response)) => match response.header("www-authenticate") { Err(Error::Status(401, response)) => match response.header("www-authenticate") {
Some(challenge) => Some(parse_www_authenticate(challenge)), Some(challenge) => Some(parse_www_authenticate(challenge)),
None => error!("Server returned invalid response!"), None => error!("Unauthorized to access registry {} and no way to authenticate was provided", registry),
}, },
Err(e) => error!("{}", e), Err(e) => error!("{}", e),
} }
} }
pub fn get_latest_digest(image: &Image, token: Option<&String>) -> Image { pub fn get_latest_digest(image: &Image, token: Option<&String>, config: &JsonValue) -> Image {
let protocol = if config["insecure_registries"].contains(json::JsonValue::from(image.registry.clone())) { "http" } else { "https" };
let mut request = ureq::head(&format!( let mut request = ureq::head(&format!(
"https://{}/v2/{}/manifests/{}", "{}://{}/v2/{}/manifests/{}",
&image.registry, &image.repository, &image.tag protocol, &image.registry, &image.repository, &image.tag
)); ));
if let Some(t) = token { if let Some(t) = token {
request = request.set("Authorization", &format!("Bearer {}", t)); request = request.set("Authorization", &format!("Bearer {}", t));
} }
let raw_response = match request let raw_response = match request
.set("Accept", "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.index.v1+json") .set("Accept", "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.index.v1+json")
.call() .call()
{ {
Ok(response) => response, Ok(response) => response,
Err(Error::Status(401, response)) => { Err(Error::Status(401, response)) => {
if token.is_some() { if token.is_some() {
error!("Failed to authenticate to registry {} with given token!\n{}", &image.registry, token.unwrap()) warn!("Failed to authenticate to registry {} with given token!\n{}", &image.registry, token.unwrap());
return Image { digest: None, ..image.clone() }
} else { } else {
return get_latest_digest( return get_latest_digest(
image, image,
Some(&get_token( Some(&get_token(
vec![image], vec![image],
&parse_www_authenticate(response.header("www-authenticate").unwrap()), &parse_www_authenticate(response.header("www-authenticate").unwrap()),
&None // I think?
)), )),
config
); );
} }
} }
@@ -63,10 +68,10 @@ pub fn get_latest_digest(image: &Image, token: Option<&String>) -> Image {
} }
} }
pub fn get_latest_digests(images: Vec<&Image>, token: Option<&String>) -> Vec<Image> { pub fn get_latest_digests(images: Vec<&Image>, token: Option<&String>, config: &JsonValue) -> Vec<Image> {
let result: Mutex<Vec<Image>> = Mutex::new(Vec::new()); let result: Mutex<Vec<Image>> = Mutex::new(Vec::new());
images.par_iter().for_each(|&image| { images.par_iter().for_each(|&image| {
let digest = get_latest_digest(image, token).digest; let digest = get_latest_digest(image, token, config).digest;
result.lock().unwrap().push(Image { result.lock().unwrap().push(Image {
digest, digest,
..image.clone() ..image.clone()
@@ -76,14 +81,17 @@ pub fn get_latest_digests(images: Vec<&Image>, token: Option<&String>) -> Vec<Im
r r
} }
pub fn get_token(images: Vec<&Image>, auth_url: &str) -> String { pub fn get_token(images: Vec<&Image>, auth_url: &str, credentials: &Option<String>) -> String {
let mut final_url = auth_url.to_owned(); let mut final_url = auth_url.to_owned();
for image in images { for image in images {
final_url = format!("{}&scope=repository:{}:pull", final_url, image.repository); final_url = format!("{}&scope=repository:{}:pull", final_url, image.repository);
} }
let raw_response = match ureq::get(&final_url) let mut base_request = ureq::get(&final_url).set("Accept", "application/vnd.oci.image.index.v1+json"); // Seems to be unnecesarry. Will probably remove in the future
.set("Accept", "application/vnd.oci.image.index.v1+json") base_request = match credentials {
.call() Some(creds) => base_request.set("Authorization", &format!("Basic {}", creds)),
None => base_request
};
let raw_response = match base_request.call()
{ {
Ok(response) => match response.into_string() { Ok(response) => match response.into_string() {
Ok(res) => res, Ok(res) => res,
@@ -118,6 +126,6 @@ fn parse_www_authenticate(www_auth: &str) -> String {
error!("Unsupported scheme {}", &challenge.scheme) error!("Unsupported scheme {}", &challenge.scheme)
} }
} else { } else {
error!("No challenge provided"); error!("No challenge provided by the server");
} }
} }

View File

@@ -94,7 +94,7 @@ impl ServerData {
s s
} }
async fn refresh(&mut self) { async fn refresh(&mut self) {
let updates = sort_update_vec(&get_all_updates(self.socket.clone()).await); let updates = sort_update_vec(&get_all_updates(self.socket.clone(), &self.config["authentication"]).await);
self.raw_updates = updates; self.raw_updates = updates;
let template = liquid::ParserBuilder::with_stdlib() let template = liquid::ParserBuilder::with_stdlib()
.build() .build()

View File

@@ -13,6 +13,14 @@ macro_rules! error {
}) })
} }
// A small macro to print in yellow as a warning
#[macro_export]
macro_rules! warn {
($($arg:tt)*) => ({
eprintln!("\x1b[93m{}\x1b[0m", format!($($arg)*));
})
}
/// Takes an image and splits it into registry, repository and tag. For example ghcr.io/sergi0g/cup:latest becomes ['ghcr.io', 'sergi0g/cup', 'latest']. /// Takes an image and splits it into registry, repository and tag. For example ghcr.io/sergi0g/cup:latest becomes ['ghcr.io', 'sergi0g/cup', 'latest'].
pub fn split_image(image: &str) -> (String, String, String) { pub fn split_image(image: &str) -> (String, String, String) {
static RE: Lazy<Regex> = Lazy::new(|| { static RE: Lazy<Regex> = Lazy::new(|| {
@@ -23,15 +31,16 @@ pub fn split_image(image: &str) -> (String, String, String) {
}); });
match RE.captures(image) { match RE.captures(image) {
Some(c) => { Some(c) => {
return ( let registry = match c.name("registry") {
match c.name("registry") {
Some(registry) => registry.as_str().to_owned(), Some(registry) => registry.as_str().to_owned(),
None => String::from("registry-1.docker.io"), None => String::from("registry-1.docker.io"),
}, };
return (
registry.clone(),
match c.name("repository") { match c.name("repository") {
Some(repository) => { Some(repository) => {
let repo = repository.as_str().to_owned(); let repo = repository.as_str().to_owned();
if !repo.contains('/') { if !repo.contains('/') && registry == "registry-1.docker.io" {
format!("library/{}", repo) format!("library/{}", repo)
} else { } else {
repo repo