mirror of
https://github.com/sergi0g/cup.git
synced 2025-11-10 14:13:49 -05:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c969ded188 | ||
|
|
53f32958fc | ||
|
|
82ec9b6e52 | ||
|
|
8ad5cbb127 | ||
|
|
7ea4c63322 | ||
|
|
30b8e943c0 | ||
|
|
0f7245dbf4 | ||
|
|
2549ed7801 | ||
|
|
90239f83e9 | ||
|
|
dc7a981930 | ||
|
|
8d2740dc7d | ||
|
|
fb674acf96 | ||
|
|
e9160334d9 | ||
|
|
ca6ffea29c | ||
|
|
923e81d75d |
34
.github/workflows/build.yml
vendored
34
.github/workflows/build.yml
vendored
@@ -18,21 +18,10 @@ jobs:
|
||||
build-binary:
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
- release_for: linux-aarch64
|
||||
os: ubuntu-latest
|
||||
target: aarch64-unknown-linux-musl
|
||||
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 }}
|
||||
arch:
|
||||
- aarch64
|
||||
- x86_64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -44,13 +33,13 @@ jobs:
|
||||
run: cargo install cross --git https://github.com/cross-rs/cross
|
||||
|
||||
- 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
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.platform.name }}
|
||||
path: target/${{ matrix.platform.target }}/release/${{ matrix.platform.bin }}
|
||||
name: cup-linux-${{ matrix.arch }}
|
||||
path: target/${{ matrix.arch }}-unknown-linux-musl/release/cup
|
||||
|
||||
build-image:
|
||||
needs: get-tag
|
||||
@@ -98,6 +87,11 @@ jobs:
|
||||
name: 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
|
||||
uses: softprops/action-gh-release@v2
|
||||
env:
|
||||
@@ -107,5 +101,5 @@ jobs:
|
||||
tag_name: ${{ needs.get-tag.outputs.tag }}
|
||||
name: ${{ needs.get-tag.outputs.tag }}
|
||||
files: |
|
||||
cup-linux-aarch64/cup-linux-aarch64
|
||||
cup-linux-x86_64/cup-linux-x86_64
|
||||
cup-linux-aarch64
|
||||
cup-linux-x86_64
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,3 +2,6 @@
|
||||
/docs/.next
|
||||
/docs/node_modules
|
||||
/docs/out
|
||||
|
||||
# In case I accidentally commit mine...
|
||||
cup.json
|
||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -350,7 +350,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cup"
|
||||
version = "1.1.3"
|
||||
version = "2.1.0"
|
||||
dependencies = [
|
||||
"bollard",
|
||||
"chrono",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cup"
|
||||
version = "1.1.3"
|
||||
version = "2.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -20,7 +20,7 @@ Cup is the easiest way to check for container image updates.
|
||||
|
||||
## Documentation
|
||||
|
||||
Take a look at https://sergi0g.github.io/cup/docs/introduction!
|
||||
Take a look at https://sergi0g.github.io/cup/docs!
|
||||
|
||||
## Limitations
|
||||
|
||||
|
||||
1
docs/.tool-versions
Normal file
1
docs/.tool-versions
Normal file
@@ -0,0 +1 @@
|
||||
nodejs 21.6.2
|
||||
@@ -2,6 +2,15 @@
|
||||
"index": {
|
||||
"title": "Introduction"
|
||||
},
|
||||
"installation": {
|
||||
"title": "Installation"
|
||||
},
|
||||
"configuration": {
|
||||
"title": "Configuration"
|
||||
},
|
||||
"usage": {
|
||||
"title": "Usage"
|
||||
},
|
||||
"nightly": {
|
||||
"title": "Using the latest version"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import Image from "next/image";
|
||||
import { Steps, Callout } from "nextra-theme-docs";
|
||||
import blue from "../../assets/blue_theme.png"
|
||||
import gray from "../../assets/gray_theme.png"
|
||||
import { Steps, Callout, Card, Cards } from "nextra-theme-docs";
|
||||
import { IconPaint, IconLockOpen, IconKey } from '@tabler/icons-react';
|
||||
|
||||
# Configuration
|
||||
|
||||
@@ -15,6 +13,8 @@ For example, if using Podman, you might do
|
||||
$ cup -s /run/user/1000/podman/podman.sock check
|
||||
```
|
||||
|
||||
This option will hopefully be moved to the configuration file soon.
|
||||
|
||||
## Configuration file
|
||||
|
||||
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_
|
||||
|
||||
### 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:
|
||||
```json
|
||||
{
|
||||
authentication: {
|
||||
"authentication": {
|
||||
"ghcr.io": "<YOUR_TOKEN_HERE>",
|
||||
"registry-1.docker.io": "<YOUR_TOKEN_HERE>"
|
||||
},
|
||||
theme: "blue"
|
||||
"theme": "blue",
|
||||
"insecure_registries": ["localhost:5000", "my-insecure-registry.example.com"]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -49,50 +56,3 @@ $ cup -c /home/sergio/.config/cup.json check
|
||||
$ 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>
|
||||
|
||||
## 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>
|
||||
22
docs/pages/docs/configuration/authentication.mdx
Normal file
22
docs/pages/docs/configuration/authentication.mdx
Normal 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>
|
||||
20
docs/pages/docs/configuration/insecure-registries.mdx
Normal file
20
docs/pages/docs/configuration/insecure-registries.mdx
Normal 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>
|
||||
31
docs/pages/docs/configuration/theme.mdx
Normal file
31
docs/pages/docs/configuration/theme.mdx
Normal 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
|
||||
}
|
||||
```
|
||||
24
src/check.rs
24
src/check.rs
@@ -1,8 +1,10 @@
|
||||
use std::{collections::{HashMap, HashSet}, sync::Mutex};
|
||||
|
||||
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};
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
use crate::docker::get_image_from_docker_daemon;
|
||||
#[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 local_images = get_images_from_docker_daemon(socket).await;
|
||||
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()
|
||||
.filter(|image| &image.registry == registry)
|
||||
.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) => {
|
||||
let token = get_token(images.clone(), &auth_url);
|
||||
get_latest_digests(images, Some(&token))
|
||||
let token = get_token(images.clone(), &auth_url, &credentials);
|
||||
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);
|
||||
}
|
||||
@@ -67,15 +70,16 @@ pub async fn get_all_updates(socket: Option<String>) -> Vec<(String, Option<bool
|
||||
}
|
||||
|
||||
#[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 token = match check_auth(&local_image.registry) {
|
||||
Some(auth_url) => get_token(vec![&local_image], &auth_url),
|
||||
let credentials = config["authentication"][&local_image.registry].clone().take_string().or(None);
|
||||
let token = match check_auth(&local_image.registry, config) {
|
||||
Some(auth_url) => get_token(vec![&local_image], &auth_url, &credentials),
|
||||
None => String::new(),
|
||||
};
|
||||
let remote_image = match token.as_str() {
|
||||
"" => get_latest_digest(&local_image, None),
|
||||
_ => get_latest_digest(&local_image, Some(&token)),
|
||||
"" => get_latest_digest(&local_image, None, config),
|
||||
_ => get_latest_digest(&local_image, Some(&token), config),
|
||||
};
|
||||
match &remote_image.digest {
|
||||
Some(d) => Some(d != &local_image.digest.unwrap()),
|
||||
|
||||
@@ -69,7 +69,7 @@ async fn main() {
|
||||
#[cfg(feature = "cli")]
|
||||
Some(Commands::Check { image, icons, raw }) => match image {
|
||||
Some(name) => {
|
||||
let has_update = get_update(name, cli.socket).await;
|
||||
let has_update = get_update(name, cli.socket, &config).await;
|
||||
match raw {
|
||||
true => print_raw_update(name, &has_update),
|
||||
false => print_update(name, &has_update),
|
||||
@@ -77,10 +77,10 @@ async fn main() {
|
||||
}
|
||||
None => {
|
||||
match raw {
|
||||
true => print_raw_updates(&get_all_updates(cli.socket).await),
|
||||
true => print_raw_updates(&get_all_updates(cli.socket, &config).await),
|
||||
false => {
|
||||
let spinner = Spinner::new();
|
||||
let updates = get_all_updates(cli.socket).await;
|
||||
let updates = get_all_updates(cli.socket, &config).await;
|
||||
spinner.succeed();
|
||||
print_updates(&updates, icons);
|
||||
}
|
||||
|
||||
@@ -6,43 +6,48 @@ use ureq::Error;
|
||||
|
||||
use http_auth::parse_challenges;
|
||||
|
||||
use crate::{error, image::Image};
|
||||
use crate::{error, image::Image, warn};
|
||||
|
||||
pub fn check_auth(registry: &str) -> Option<String> {
|
||||
let response = ureq::get(&format!("https://{}/v2/", registry)).call();
|
||||
pub fn check_auth(registry: &str, config: &JsonValue) -> Option<String> {
|
||||
let protocol = if config["insecure_registries"].contains(registry) { "http" } else { "https" };
|
||||
let response = ureq::get(&format!("{}://{}/v2/", protocol, registry)).call();
|
||||
match response {
|
||||
Ok(_) => None,
|
||||
Err(Error::Status(401, response)) => match response.header("www-authenticate") {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
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!(
|
||||
"https://{}/v2/{}/manifests/{}",
|
||||
&image.registry, &image.repository, &image.tag
|
||||
"{}://{}/v2/{}/manifests/{}",
|
||||
protocol, &image.registry, &image.repository, &image.tag
|
||||
));
|
||||
if let Some(t) = token {
|
||||
request = request.set("Authorization", &format!("Bearer {}", t));
|
||||
}
|
||||
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()
|
||||
{
|
||||
Ok(response) => response,
|
||||
Err(Error::Status(401, response)) => {
|
||||
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 {
|
||||
return get_latest_digest(
|
||||
image,
|
||||
Some(&get_token(
|
||||
vec![image],
|
||||
&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());
|
||||
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 {
|
||||
digest,
|
||||
..image.clone()
|
||||
@@ -76,14 +81,17 @@ pub fn get_latest_digests(images: Vec<&Image>, token: Option<&String>) -> Vec<Im
|
||||
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();
|
||||
for image in images {
|
||||
final_url = format!("{}&scope=repository:{}:pull", final_url, image.repository);
|
||||
}
|
||||
let raw_response = match ureq::get(&final_url)
|
||||
.set("Accept", "application/vnd.oci.image.index.v1+json")
|
||||
.call()
|
||||
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
|
||||
base_request = match credentials {
|
||||
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(res) => res,
|
||||
@@ -118,6 +126,6 @@ fn parse_www_authenticate(www_auth: &str) -> String {
|
||||
error!("Unsupported scheme {}", &challenge.scheme)
|
||||
}
|
||||
} else {
|
||||
error!("No challenge provided");
|
||||
error!("No challenge provided by the server");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ impl ServerData {
|
||||
s
|
||||
}
|
||||
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;
|
||||
let template = liquid::ParserBuilder::with_stdlib()
|
||||
.build()
|
||||
|
||||
17
src/utils.rs
17
src/utils.rs
@@ -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'].
|
||||
pub fn split_image(image: &str) -> (String, String, String) {
|
||||
static RE: Lazy<Regex> = Lazy::new(|| {
|
||||
@@ -23,15 +31,16 @@ pub fn split_image(image: &str) -> (String, String, String) {
|
||||
});
|
||||
match RE.captures(image) {
|
||||
Some(c) => {
|
||||
return (
|
||||
match c.name("registry") {
|
||||
let registry = match c.name("registry") {
|
||||
Some(registry) => registry.as_str().to_owned(),
|
||||
None => String::from("registry-1.docker.io"),
|
||||
},
|
||||
};
|
||||
return (
|
||||
registry.clone(),
|
||||
match c.name("repository") {
|
||||
Some(repository) => {
|
||||
let repo = repository.as_str().to_owned();
|
||||
if !repo.contains('/') {
|
||||
if !repo.contains('/') && registry == "registry-1.docker.io" {
|
||||
format!("library/{}", repo)
|
||||
} else {
|
||||
repo
|
||||
|
||||
Reference in New Issue
Block a user