Add new client Podman to the pull command (#18857)

1.Fixes #18832

Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
Shijun Sun 2023-06-27 11:12:41 +08:00 committed by GitHub
parent 1d6c02f52d
commit d36ca805b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 439 additions and 148 deletions

View File

@ -126,6 +126,12 @@
</clr-dropdown>
</div>
<div class="right-pos">
<app-pull-command
[registryUrl]="registryUrl"
[projectName]="projectName"
[repoName]="repoName"
[selectedRow]="selectedRow"
class="mr-1"></app-pull-command>
<app-artifact-filter
[withDivider]="true"
(filterEvent)="filterEvent($event)"
@ -142,11 +148,6 @@
{{ 'REPOSITORY.ARTIFACTS_COUNT' | translate }}
</ng-template>
</clr-dg-column>
<clr-dg-column class="pull-command-column">
<ng-template [clrDgHideableColumn]="{ hidden: hiddenArray[1] }">
{{ 'REPOSITORY.PULL_COMMAND' | translate }}
</ng-template>
</clr-dg-column>
<clr-dg-column *ngIf="depth">
<ng-template [clrDgHideableColumn]="{ hidden: hiddenArray[2] }">
{{ 'REPOSITORY.PLATFORM' | translate }}
@ -244,15 +245,6 @@
</clr-tooltip>
</div>
</clr-dg-cell>
<clr-dg-cell>
<hbr-copy-input
[title]="getPullCommand(artifact)"
*ngIf="getPullCommand(artifact)"
[iconMode]="true"
[defaultValue]="
getPullCommand(artifact)
"></hbr-copy-input>
</clr-dg-cell>
<clr-dg-cell *ngIf="depth">
<div class="cell">
{{ artifact.platform?.os }}

View File

@ -57,9 +57,6 @@ import {
ArtifactFilterEvent,
ArtifactFront as Artifact,
ArtifactFront,
ArtifactType,
getPullCommandByDigest,
getPullCommandByTag,
} from '../../../artifact';
import { Project } from '../../../../../project';
import { ArtifactService as NewArtifactService } from '../../../../../../../../../ng-swagger-gen/services/artifact.service';
@ -458,32 +455,6 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
this.clrLoad(st);
}
getPullCommand(artifact: Artifact): string {
let pullCommand: string = '';
if (
artifact.type === ArtifactType.CHART &&
artifact.tags &&
artifact.tags[0]
) {
pullCommand = getPullCommandByTag(
artifact.type,
`${this.registryUrl ? this.registryUrl : location.hostname}/${
this.projectName
}/${this.repoName}`,
artifact.tags[0]?.name
);
} else {
pullCommand = getPullCommandByDigest(
artifact.type,
`${this.registryUrl ? this.registryUrl : location.hostname}/${
this.projectName
}/${this.repoName}`,
artifact.digest
);
}
return pullCommand;
}
canAddLabel(): boolean {
if (this.selectedRow && this.selectedRow.length === 1) {
return true;

View File

@ -0,0 +1,131 @@
<clr-dropdown [clrCloseMenuOnItemClick]="false" class="mr-1" *ngIf="!isTagMode">
<button
[disabled]="
selectedRow?.length !== 1 || !hasPullCommand(selectedRow[0])
"
class="btn btn-link copy-pull-command"
clrDropdownTrigger>
{{ 'PUSH_IMAGE.COPY_PULL_COMMAND' | translate }}
<cds-icon shape="angle" direction="down"></cds-icon>
</button>
<clr-dropdown-menu clrPosition="bottom-right" *clrIfOpen>
<ng-container *ngIf="isImage(selectedRow[0])">
<div
class="flex"
aria-label="Dropdown header Action"
clrDropdownItem>
<hbr-copy-input
[title]="getPullCommandForDocker(selectedRow[0])"
[iconMode]="true"
[defaultValue]="
getPullCommandForDocker(selectedRow[0])
"></hbr-copy-input>
<span>{{ 'PUSH_IMAGE.DOCKER' | translate }}</span>
</div>
<div
class="flex"
aria-label="Dropdown header Action"
clrDropdownItem>
<hbr-copy-input
[title]="getPullCommandForPadMan(selectedRow[0])"
[iconMode]="true"
[defaultValue]="
getPullCommandForPadMan(selectedRow[0])
"></hbr-copy-input>
<span>{{ 'PUSH_IMAGE.PODMAN' | translate }}</span>
</div>
</ng-container>
<div
*ngIf="isCNAB(selectedRow[0])"
class="flex"
aria-label="Dropdown header Action"
clrDropdownItem>
<hbr-copy-input
[title]="getPullCommandForCNAB(selectedRow[0])"
[iconMode]="true"
[defaultValue]="
getPullCommandForCNAB(selectedRow[0])
"></hbr-copy-input>
<span>{{ 'PUSH_IMAGE.CNAB' | translate }}</span>
</div>
<div
*ngIf="isChart(selectedRow[0])"
class="flex"
aria-label="Dropdown header Action"
clrDropdownItem>
<hbr-copy-input
[title]="getPullCommandForChart(selectedRow[0])"
[iconMode]="true"
[defaultValue]="
getPullCommandForChart(selectedRow[0])
"></hbr-copy-input>
<span>{{ 'PUSH_IMAGE.HELM' | translate }}</span>
</div>
</clr-dropdown-menu>
</clr-dropdown>
<clr-dropdown [clrCloseMenuOnItemClick]="false" class="mr-1" *ngIf="isTagMode">
<button
[disabled]="
selectedTags?.length !== 1 || !hasPullCommandForTag(artifact)
"
class="btn btn-link copy-pull-command"
clrDropdownTrigger>
{{ 'PUSH_IMAGE.COPY_PULL_COMMAND' | translate }}
<cds-icon shape="angle" direction="down"></cds-icon>
</button>
<clr-dropdown-menu clrPosition="bottom-right" *clrIfOpen>
<ng-container *ngIf="isImage(artifact)">
<div
class="flex"
aria-label="Dropdown header Action"
clrDropdownItem>
<hbr-copy-input
[title]="getPullCommandForDockerByTag(artifact)"
[iconMode]="true"
[defaultValue]="
getPullCommandForDockerByTag(artifact)
"></hbr-copy-input>
<span>{{ 'PUSH_IMAGE.DOCKER' | translate }}</span>
</div>
<div
class="flex"
aria-label="Dropdown header Action"
clrDropdownItem>
<hbr-copy-input
[title]="getPullCommandForPadManByTag(artifact)"
[iconMode]="true"
[defaultValue]="
getPullCommandForPadManByTag(artifact)
"></hbr-copy-input>
<span>{{ 'PUSH_IMAGE.PODMAN' | translate }}</span>
</div>
</ng-container>
<div
*ngIf="isCNAB(artifact)"
class="flex"
aria-label="Dropdown header Action"
clrDropdownItem>
<hbr-copy-input
[title]="getPullCommandForCNABByTag(artifact)"
[iconMode]="true"
[defaultValue]="
getPullCommandForCNABByTag(artifact)
"></hbr-copy-input>
<span>{{ 'PUSH_IMAGE.CNAB' | translate }}</span>
</div>
<div
*ngIf="isChart(artifact)"
class="flex"
aria-label="Dropdown header Action"
clrDropdownItem>
<hbr-copy-input
[title]="getPullCommandForChartByTag(artifact)"
[iconMode]="true"
[defaultValue]="
getPullCommandForChartByTag(artifact)
"></hbr-copy-input>
<span>{{ 'PUSH_IMAGE.HELM' | translate }}</span>
</div>
</clr-dropdown-menu>
</clr-dropdown>

View File

@ -0,0 +1,8 @@
.copy-pull-command {
font-size: 14px;
}
.flex {
display: flex;
align-items: flex-end;
}

View File

@ -0,0 +1,22 @@
import { PullCommandComponent } from './pull-command.component';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SharedTestingModule } from '../../../../../../../../shared/shared.module';
describe('PullCommandComponent', () => {
let component: PullCommandComponent;
let fixture: ComponentFixture<PullCommandComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [PullCommandComponent],
imports: [SharedTestingModule],
}).compileComponents();
fixture = TestBed.createComponent(PullCommandComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,152 @@
import { Component, Input } from '@angular/core';
import {
AccessoryType,
ArtifactFront as Artifact,
ArtifactType,
Clients,
getPullCommandByDigest,
getPullCommandByTag,
hasPullCommand,
} from '../../../../artifact';
import { Tag } from '../../../../../../../../../../ng-swagger-gen/models/tag';
@Component({
selector: 'app-pull-command',
templateUrl: './pull-command.component.html',
styleUrls: ['./pull-command.component.scss'],
})
export class PullCommandComponent {
@Input()
isTagMode: boolean = false; // tagMode is for tag list datagrid,
@Input()
projectName: string;
@Input()
registryUrl: string;
@Input()
repoName: string;
@Input()
selectedRow: Artifact[];
// for tagMode
@Input()
selectedTags: Tag[];
@Input()
artifact: Artifact;
@Input()
accessoryType: string;
hasPullCommand(artifact: Artifact): boolean {
return hasPullCommand(artifact);
}
isImage(artifact: Artifact): boolean {
return artifact.type === ArtifactType.IMAGE;
}
isCNAB(artifact: Artifact): boolean {
return artifact.type === ArtifactType.CNAB;
}
isChart(artifact: Artifact): boolean {
return artifact.type === ArtifactType.CHART;
}
getPullCommandForDocker(artifact: Artifact): string {
return getPullCommandByDigest(
artifact.type,
`${this.registryUrl ? this.registryUrl : location.hostname}/${
this.projectName
}/${this.repoName}`,
artifact.digest,
Clients.DOCKER
);
}
getPullCommandForPadMan(artifact: Artifact): string {
return getPullCommandByDigest(
artifact.type,
`${this.registryUrl ? this.registryUrl : location.hostname}/${
this.projectName
}/${this.repoName}`,
artifact.digest,
Clients.PODMAN
);
}
getPullCommandForCNAB(artifact: Artifact): string {
return getPullCommandByDigest(
artifact.type,
`${this.registryUrl ? this.registryUrl : location.hostname}/${
this.projectName
}/${this.repoName}`,
artifact.digest,
Clients.CNAB
);
}
getPullCommandForChart(artifact: Artifact): string {
return getPullCommandByTag(
artifact.type,
`${this.registryUrl ? this.registryUrl : location.hostname}/${
this.projectName
}/${this.repoName}`,
artifact.tags[0].name,
Clients.CHART
);
}
// For tagMode
hasPullCommandForTag(artifact): boolean {
return (
(artifact?.type === ArtifactType.IMAGE ||
artifact?.type === ArtifactType.CHART ||
artifact?.type === ArtifactType.CNAB) &&
this.accessoryType !== AccessoryType.COSIGN &&
this.accessoryType !== AccessoryType.NYDUS
);
}
getPullCommandForDockerByTag(artifact: Artifact): string {
return getPullCommandByTag(
artifact.type,
`${this.registryUrl ? this.registryUrl : location.hostname}/${
this.projectName
}/${this.repoName}`,
this.selectedTags[0].name,
Clients.DOCKER
);
}
getPullCommandForPadManByTag(artifact: Artifact): string {
return getPullCommandByTag(
artifact.type,
`${this.registryUrl ? this.registryUrl : location.hostname}/${
this.projectName
}/${this.repoName}`,
this.selectedTags[0].name,
Clients.PODMAN
);
}
getPullCommandForCNABByTag(artifact: Artifact): string {
return getPullCommandByTag(
artifact.type,
`${this.registryUrl ? this.registryUrl : location.hostname}/${
this.projectName
}/${this.repoName}`,
this.selectedTags[0].name,
Clients.CNAB
);
}
getPullCommandForChartByTag(artifact: Artifact): string {
return getPullCommandByTag(
artifact.type,
`${this.registryUrl ? this.registryUrl : location.hostname}/${
this.projectName
}/${this.repoName}`,
this.selectedTags[0].name,
Clients.CHART
);
}
}

View File

@ -5,36 +5,52 @@
(clrDgRefresh)="getCurrentArtifactTags($event)"
[(clrDgSelected)]="selectedRow">
<clr-dg-action-bar>
<button
*ngIf="!isProxyCacheProject"
id="new-tag"
type="button"
[disabled]="!hasCreateTagPermission"
class="btn btn-secondary"
(click)="addTag()">
<clr-icon shape="plus" size="16"></clr-icon>&nbsp;{{
'TAG.ADD_TAG' | translate
}}
</button>
<button
id="delete-tag"
type="button"
class="btn btn-secondary"
[disabled]="
!(
selectedRow.length >= 1 &&
!hasImmutableOnTag() &&
hasDeleteTagPermission
)
"
(click)="removeTag()">
<clr-icon shape="trash" size="16"></clr-icon>&nbsp;{{
'TAG.REMOVE_TAG' | translate
}}
</button>
<span class="refresh-btn" (click)="refresh()">
<clr-icon shape="refresh"></clr-icon>
</span>
<div class="action-bar">
<div>
<button
*ngIf="!isProxyCacheProject"
id="new-tag"
type="button"
[disabled]="!hasCreateTagPermission"
class="btn btn-secondary"
(click)="addTag()">
<clr-icon shape="plus" size="16"></clr-icon>&nbsp;{{
'TAG.ADD_TAG' | translate
}}
</button>
<button
id="delete-tag"
type="button"
class="btn btn-secondary"
[disabled]="
!(
selectedRow.length >= 1 &&
!hasImmutableOnTag() &&
hasDeleteTagPermission
)
"
(click)="removeTag()">
<clr-icon shape="trash" size="16"></clr-icon>&nbsp;{{
'TAG.REMOVE_TAG' | translate
}}
</button>
</div>
<div class="right-pos">
<app-pull-command
[isTagMode]="true"
[artifact]="artifactDetails"
[accessoryType]="accessoryType"
[registryUrl]="registryUrl"
[projectName]="projectName"
[repoName]="repositoryName"
[selectedTags]="selectedRow"
class="mr-1"></app-pull-command>
<span class="refresh-btn" (click)="refresh()">
<clr-icon shape="refresh"></clr-icon>
</span>
</div>
</div>
<form
#tagForm="ngForm"
[hidden]="!newTagformShow"
@ -102,9 +118,6 @@
<clr-dg-column [clrDgField]="'name'">{{
'TAG.NAME' | translate
}}</clr-dg-column>
<clr-dg-column *ngIf="hasPullCommand()">{{
'REPOSITORY.PULL_COMMAND' | translate
}}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="'pull_time'">{{
'TAG.PULL_TIME' | translate
}}</clr-dg-column>
@ -128,13 +141,6 @@
}}</span>
</div>
</clr-dg-cell>
<clr-dg-cell *ngIf="hasPullCommand()">
<hbr-copy-input
[title]="getPullCommand(tag)"
*ngIf="getPullCommand(tag)"
[iconMode]="true"
[defaultValue]="getPullCommand(tag)"></hbr-copy-input>
</clr-dg-cell>
<clr-dg-cell>{{
tag.pull_time !== availableTime
? (tag.pull_time | harborDatetime : 'short')

View File

@ -39,9 +39,6 @@
}
.refresh-btn {
float: right;
margin-top: 15px;
margin-right: 20px;
cursor: pointer;
}
@ -50,3 +47,15 @@
right: -.7rem;
top: 1.2rem;
}
.action-bar {
display: flex;
align-items: center;
justify-content: space-between;
}
.right-pos {
margin-right: 35px;
display: flex;
align-items: center;
}

View File

@ -23,13 +23,7 @@ import {
OperateInfo,
OperationState,
} from '../../../../../shared/components/operation/operate';
import {
AccessoryQueryParams,
AccessoryType,
ArtifactFront as Artifact,
ArtifactType,
getPullCommandByTag,
} from '../artifact';
import { AccessoryQueryParams, ArtifactFront as Artifact } from '../artifact';
import { ArtifactService } from '../../../../../../../ng-swagger-gen/services/artifact.service';
import { Tag } from '../../../../../../../ng-swagger-gen/models/tag';
import {
@ -49,7 +43,6 @@ import {
PageSizeMapKeys,
setPageSizeToLocalStorage,
} from '../../../../../shared/units/utils';
import { AppConfigService } from '../../../../../services/app-config.service';
import { errorHandler } from '../../../../../shared/units/shared.utils';
import { ConfirmationDialogComponent } from '../../../../../shared/components/confirmation-dialog';
import { ConfirmationMessage } from '../../../../global-confirmation-dialog/confirmation-message';
@ -59,8 +52,6 @@ import { ActivatedRoute } from '@angular/router';
class InitTag {
name = '';
}
const DeleteTagWithNotoryCommand1 = 'notary -s https://';
const DeleteTagWithNotoryCommand2 = ':4443 -d ~/.docker/trust remove -p ';
@Component({
selector: 'artifact-tag',
templateUrl: './artifact-tag.component.html',
@ -73,7 +64,6 @@ export class ArtifactTagComponent implements OnInit, OnDestroy {
@Input() projectId: number;
@Input() repositoryName: string;
newTagName = new InitTag();
newTagForm: NgForm;
@ViewChild('newTagForm', { static: true }) currentForm: NgForm;
selectedRow: Tag[] = [];
isTagNameExist = false;
@ -87,7 +77,6 @@ export class ArtifactTagComponent implements OnInit, OnDestroy {
hasCreateTagPermission: boolean;
totalCount: number = 0;
allTags: Tag[] = [];
currentTags: Tag[] = [];
pageSize: number = getPageSizeFromLocalStorage(
PageSizeMapKeys.ARTIFACT_TAGS_COMPONENT
@ -341,12 +330,6 @@ export class ArtifactTagComponent implements OnInit, OnDestroy {
}
}
}
deletePort(url): string {
if (url && url.indexOf(':') !== -1) {
return url.split(':')[0];
}
return url;
}
delOperate(tag: Tag): Observable<any> | null {
// init operation info
let operMessage = new OperateInfo();
@ -387,10 +370,6 @@ export class ArtifactTagComponent implements OnInit, OnDestroy {
this.isTagNameExist = false;
}
}
toggleTagListOpenOrClose() {
this.openTag = !this.openTag;
this.newTagformShow = false;
}
hasImmutableOnTag(): boolean {
return this.selectedRow.some(artifact => artifact.immutable);
}
@ -413,25 +392,4 @@ export class ArtifactTagComponent implements OnInit, OnDestroy {
}
return location.hostname;
}
hasPullCommand(): boolean {
return (
this.artifactDetails &&
(this.artifactDetails.type === ArtifactType.IMAGE ||
this.artifactDetails.type === ArtifactType.CHART ||
this.artifactDetails.type === ArtifactType.CNAB) &&
this.accessoryType !== AccessoryType.COSIGN &&
this.accessoryType !== AccessoryType.NYDUS
);
}
getPullCommand(tag: Tag): string {
let pullCommand: string = '';
if (tag && tag.name && this.artifactDetails) {
pullCommand = getPullCommandByTag(
this.artifactDetails?.type,
`${this.registryUrl}/${this.projectName}/${this.repositoryName}`,
tag.name
);
}
return pullCommand;
}
}

View File

@ -24,6 +24,7 @@ import { ArtifactListPageService } from './artifact-list-page/artifact-list-page
import { CopyArtifactComponent } from './artifact-list-page/artifact-list/artifact-list-tab/copy-artifact/copy-artifact.component';
import { CopyDigestComponent } from './artifact-list-page/artifact-list/artifact-list-tab/copy-digest/copy-digest.component';
import { ArtifactFilterComponent } from './artifact-list-page/artifact-list/artifact-list-tab/artifact-filter/artifact-filter.component';
import { PullCommandComponent } from './artifact-list-page/artifact-list/artifact-list-tab/pull-command/pull-command.component';
const routes: Routes = [
{
@ -88,6 +89,7 @@ const routes: Routes = [
CopyArtifactComponent,
CopyDigestComponent,
ArtifactFilterComponent,
PullCommandComponent,
],
imports: [RouterModule.forChild(routes), SharedModule],
providers: [

View File

@ -89,41 +89,59 @@ export enum AccessoryQueryParams {
ACCESSORY_TYPE = 'accessoryType',
}
export function hasPullCommand(artifact: Artifact): boolean {
return (
artifact.type === ArtifactType.IMAGE ||
artifact.type === ArtifactType.CNAB ||
artifact.type === ArtifactType.CHART
);
}
export function getPullCommandByDigest(
artifactType: string,
url: string,
digest: string
digest: string,
client: Clients
): string {
let pullCommand: string = '';
if (artifactType && url && digest) {
if (artifactType === ArtifactType.IMAGE) {
pullCommand = `docker pull ${url}@${digest}`;
if (client === Clients.DOCKER) {
return `${Clients.DOCKER} pull ${url}@${digest}`;
}
if (client === Clients.PODMAN) {
return `${Clients.PODMAN} pull ${url}@${digest}`;
}
}
if (artifactType === ArtifactType.CNAB) {
pullCommand = `cnab-to-oci pull ${url}@${digest}`;
return `${Clients.CNAB} pull ${url}@${digest}`;
}
}
return pullCommand;
return null;
}
export function getPullCommandByTag(
artifactType: string,
url: string,
tag: string
tag: string,
client: Clients
): string {
let pullCommand: string = '';
if (artifactType && url && tag) {
if (artifactType === ArtifactType.IMAGE) {
pullCommand = `docker pull ${url}:${tag}`;
if (client === Clients.DOCKER) {
return `${Clients.DOCKER} pull ${url}:${tag}`;
}
if (client === Clients.PODMAN) {
return `${Clients.PODMAN} pull ${url}:${tag}`;
}
}
if (artifactType === ArtifactType.CNAB) {
pullCommand = `cnab-to-oci pull ${url}:${tag}`;
return `cnab-to-oci pull ${url}:${tag}`;
}
if (artifactType === ArtifactType.CHART) {
pullCommand = `helm pull oci://${url} --version ${tag}`;
return `helm pull oci://${url} --version ${tag}`;
}
}
return pullCommand;
return null;
}
export interface ArtifactFilterEvent {
@ -133,3 +151,17 @@ export interface ArtifactFilterEvent {
isInputTag?: boolean;
label?: Label;
}
export enum Clients {
DOCKER = 'docker',
PODMAN = 'podman',
CHART = 'helm',
CNAB = 'cnab-to-oci',
}
export enum ClientNames {
DOCKER = 'Docker',
PODMAN = 'Podman',
CHART = 'Helm',
CNAB = 'CNAB',
}

View File

@ -1044,7 +1044,8 @@
"TOOLTIP": "Befehlreferenz um ein Artefakt in das Projekt zu pushen.",
"TAG_COMMAND": "Tag ein Image für dieses Projekt:",
"PUSH_COMMAND": "Push ein Image für dieses Projekt:",
"COPY_ERROR": "Kopieren fehlgeschlagen, bitte die Befehlsreferenz manuell kopieren."
"COPY_ERROR": "Kopieren fehlgeschlagen, bitte die Befehlsreferenz manuell kopieren.",
"COPY_PULL_COMMAND": "COPY PULL COMMAND"
},
"ARTIFACT": {
"FILTER_FOR_ARTIFACTS": "Filter Artefakte",

View File

@ -1045,7 +1045,8 @@
"TOOLTIP": "Command references for pushing an artifact to this project.",
"TAG_COMMAND": "Tag an image for this project:",
"PUSH_COMMAND": "Push an image to this project:",
"COPY_ERROR": "Copy failed, please try to manually copy the command references."
"COPY_ERROR": "Copy failed, please try to manually copy the command references.",
"COPY_PULL_COMMAND": "COPY PULL COMMAND"
},
"ARTIFACT": {
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",

View File

@ -1043,7 +1043,8 @@
"TOOLTIP": "Command references for pushing an artifact to this project.",
"TAG_COMMAND": "Tag an image for this project:",
"PUSH_COMMAND": "Push an image to this project:",
"COPY_ERROR": "Copy failed, please try to manually copy the command references."
"COPY_ERROR": "Copy failed, please try to manually copy the command references.",
"COPY_PULL_COMMAND": "COPY PULL COMMAND"
},
"ARTIFACT": {
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",

View File

@ -1016,7 +1016,8 @@
"TOOLTIP": "Commandes pour push un artefact dans ce projet.",
"TAG_COMMAND": "Taguer une image pour ce projet :",
"PUSH_COMMAND": "Push une image dans ce projet :",
"COPY_ERROR": "Copie échouée, veuillez essayer de copier manuellement les commandes de référence."
"COPY_ERROR": "Copie échouée, veuillez essayer de copier manuellement les commandes de référence.",
"COPY_PULL_COMMAND": "COPY PULL COMMAND"
},
"ARTIFACT": {
"FILTER_FOR_ARTIFACTS": "Filtrer les artefacts",

View File

@ -1041,7 +1041,8 @@
"TOOLTIP": "Referência de comandos para enviar artefatos a este projeto.",
"TAG_COMMAND": "Colocar tag em uma imagem deste projeto:",
"PUSH_COMMAND": "Envia uma imagem a esse projeto:",
"COPY_ERROR": "Cópia falhou, por favor tente copiar o comando de referência manualmente."
"COPY_ERROR": "Cópia falhou, por favor tente copiar o comando de referência manualmente.",
"COPY_PULL_COMMAND": "COPY PULL COMMAND"
},
"ARTIFACT": {
"FILTER_FOR_ARTIFACTS": "Filtrar",

View File

@ -1044,7 +1044,8 @@
"TOOLTIP": "Command references for pushing an artifact to this project.",
"TAG_COMMAND": "Bu proje için bir imaj etiketleyin:",
"PUSH_COMMAND": "Bu projeye bir imaj gönder:",
"COPY_ERROR": "Kopyalama başarısız oldu, lütfen komut referanslarını el ile kopyalamayı deneyin."
"COPY_ERROR": "Kopyalama başarısız oldu, lütfen komut referanslarını el ile kopyalamayı deneyin.",
"COPY_PULL_COMMAND": "COPY PULL COMMAND"
},
"ARTIFACT": {
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",

View File

@ -1043,7 +1043,8 @@
"TOOLTIP": "推送一个 artifact 到当前项目的参考命令。",
"TAG_COMMAND": "在项目中标记镜像:",
"PUSH_COMMAND": "推送镜像到当前项目:",
"COPY_ERROR": "拷贝失败,请尝试手动拷贝参考命令。"
"COPY_ERROR": "拷贝失败,请尝试手动拷贝参考命令。",
"COPY_PULL_COMMAND": "复制拉取命令"
},
"ARTIFACT": {
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",

View File

@ -1038,7 +1038,8 @@
"TOOLTIP": "將映像檔推送至此專案的參考命令。",
"TAG_COMMAND": "在項目中標記映像檔:",
"PUSH_COMMAND": "推送映像檔到目前項目:",
"COPY_ERROR": "複製失敗,請嘗試手動複製參考命令。"
"COPY_ERROR": "複製失敗,請嘗試手動複製參考命令。",
"COPY_PULL_COMMAND": "COPY PULL COMMAND"
},
"ARTIFACT": {
"FILTER_FOR_ARTIFACTS": "篩選器",