mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-25 08:31:03 +00:00
Compare commits
163 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e7aadc207 | ||
|
|
f5ff116af9 | ||
|
|
3974f6fec1 | ||
|
|
697cd441aa | ||
|
|
9134abeefd | ||
|
|
8106125441 | ||
|
|
b17d474148 | ||
|
|
e3c1c8630a | ||
|
|
ebce4fc631 | ||
|
|
2ebe3848d6 | ||
|
|
b65137addf | ||
|
|
26ce871368 | ||
|
|
577797d9ed | ||
|
|
3c9d134d05 | ||
|
|
f4fc3669ca | ||
|
|
322e10dcb5 | ||
|
|
3580e04b8d | ||
|
|
b38db60ead | ||
|
|
43db69da00 | ||
|
|
78edbf3d2e | ||
|
|
753c0269d7 | ||
|
|
4137d216aa | ||
|
|
826f0293f1 | ||
|
|
c7b58683ae | ||
|
|
5e0aeec310 | ||
|
|
992c138844 | ||
|
|
c8d2fbdfdc | ||
|
|
f023b42c89 | ||
|
|
123af388b0 | ||
|
|
3314ecb66c | ||
|
|
4e2f26b21e | ||
|
|
8a1975ef4a | ||
|
|
4332656606 | ||
|
|
99bf41c873 | ||
|
|
587bbbf4b5 | ||
|
|
46400c4d32 | ||
|
|
8ee57bffaf | ||
|
|
bedde4a037 | ||
|
|
07558afdc8 | ||
|
|
1178381e00 | ||
|
|
c45bdea3a0 | ||
|
|
15677ac3d2 | ||
|
|
b8a3ca1550 | ||
|
|
be05115352 | ||
|
|
e2bc42f4bb | ||
|
|
98721a3a2c | ||
|
|
a8e75ecfe3 | ||
|
|
86cce84955 | ||
|
|
eaecd2c573 | ||
|
|
de40167712 | ||
|
|
df6c8c196e | ||
|
|
4f375bbf36 | ||
|
|
0849b800ff | ||
|
|
c73b3f7cc0 | ||
|
|
77a4de4819 | ||
|
|
2d16389add | ||
|
|
0232610967 | ||
|
|
c84bb887a4 | ||
|
|
ff579d8713 | ||
|
|
463ddfd6ee | ||
|
|
6f85b653d4 | ||
|
|
966ad05338 | ||
|
|
8fe90830b1 | ||
|
|
bb5d70f787 | ||
|
|
3daea1ea4d | ||
|
|
e27d4878a2 | ||
|
|
61cd92bc0c | ||
|
|
8ca3166439 | ||
|
|
6217ba0808 | ||
|
|
b010ea4347 | ||
|
|
ed600c7116 | ||
|
|
015a1086b7 | ||
|
|
f6c044d74f | ||
|
|
06ab7b77b1 | ||
|
|
89020d6ead | ||
|
|
1cf2e0284c | ||
|
|
58d5a18892 | ||
|
|
1e34a9babc | ||
|
|
152b365771 | ||
|
|
55b21dfd30 | ||
|
|
be2d80d015 | ||
|
|
cdf1d9ad11 | ||
|
|
968b09e6c4 | ||
|
|
2729aafe2c | ||
|
|
ce780b8f6d | ||
|
|
7f0f4305a9 | ||
|
|
9440a10053 | ||
|
|
0f1b8c9848 | ||
|
|
630ea7d324 | ||
|
|
8a0fcf99f4 | ||
|
|
623fdb4f33 | ||
|
|
bca15cf6b1 | ||
|
|
a6ade7e78d | ||
|
|
51b3bb5418 | ||
|
|
dbf2bced5e | ||
|
|
67441aa4b7 | ||
|
|
42b7ba18ea | ||
|
|
b210264c05 | ||
|
|
80af149b02 | ||
|
|
8aa725f06f | ||
|
|
559265df4a | ||
|
|
1060e17731 | ||
|
|
8976f10120 | ||
|
|
e860416ee4 | ||
|
|
cce9d40f43 | ||
|
|
6473810fa9 | ||
|
|
f25a5ebff7 | ||
|
|
b2525f4629 | ||
|
|
2dda39dff0 | ||
|
|
39e694f496 | ||
|
|
e69449c824 | ||
|
|
966570ce6c | ||
|
|
d2fa5e9e9c | ||
|
|
649e5e1c90 | ||
|
|
722fb88792 | ||
|
|
d2dd4db582 | ||
|
|
953e56065d | ||
|
|
42a9668e55 | ||
|
|
8f77c0f7c0 | ||
|
|
9e90d2f4f2 | ||
|
|
d68ecbf5f2 | ||
|
|
50582db9b7 | ||
|
|
604ca8f0df | ||
|
|
71e8e43b06 | ||
|
|
c635266fae | ||
|
|
937a6a9c93 | ||
|
|
e8e808ff47 | ||
|
|
454eaee2cf | ||
|
|
f0ee38f7f7 | ||
|
|
7c074d25f7 | ||
|
|
dffe6970aa | ||
|
|
07f26c1e91 | ||
|
|
66f5d176d3 | ||
|
|
ab8d87e6ae | ||
|
|
e5ca391291 | ||
|
|
15bc01b9af | ||
|
|
2a9ed70896 | ||
|
|
fcc0bbe552 | ||
|
|
f93ddeceef | ||
|
|
0066c1c76c | ||
|
|
cd11c3e131 | ||
|
|
62095473c0 | ||
|
|
cdd43478e2 | ||
|
|
afacc9be9e | ||
|
|
3c3c80d570 | ||
|
|
866bcae509 | ||
|
|
966a266c68 | ||
|
|
8a095f74b1 | ||
|
|
ce95ddea2a | ||
|
|
1746bc07c7 | ||
|
|
17fcdb2b20 | ||
|
|
338b655c0a | ||
|
|
6025912336 | ||
|
|
d77b895319 | ||
|
|
8871edc1a0 | ||
|
|
553b9857cd | ||
|
|
d777bfe655 | ||
|
|
821cd81b72 | ||
|
|
b8969fd55f | ||
|
|
83acb91d83 | ||
|
|
b0cef73dc1 | ||
|
|
642abae037 | ||
|
|
6fbf8c3cc5 |
1202 changed files with 97859 additions and 39257 deletions
15
.claude/settings.json
Normal file
15
.claude/settings.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Edit|Write|MultiEdit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "jq -r '.tool_input.file_path // empty' | { read -r f; case \"$f\" in *.go) gofumpt -w \"$f\" ;; esac; } 2>/dev/null || true"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
11
.devcontainer/Dockerfile
Normal file
11
.devcontainer/Dockerfile
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
FROM mcr.microsoft.com/devcontainers/go:bullseye
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN go install mvdan.cc/gofumpt@latest
|
||||
ENV PATH="/root/go/bin:${PATH}"
|
||||
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.50.0
|
||||
RUN golangci-lint --version
|
||||
54
.devcontainer/devcontainer.json
Normal file
54
.devcontainer/devcontainer.json
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"name": "Docker in Docker",
|
||||
"build": {
|
||||
"dockerfile": "./Dockerfile",
|
||||
"context": "."
|
||||
},
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/common-utils:1": {
|
||||
"installZsh": "true",
|
||||
"upgradePackages": "false",
|
||||
"uid": "1000",
|
||||
"gid": "1000",
|
||||
"installOhMyZsh": "true",
|
||||
"nonFreePackages": "true"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/docker-from-docker:1": {
|
||||
"version": "latest",
|
||||
"enableNonRootDocker": "true",
|
||||
"moby": "true"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/git:1": {},
|
||||
"ghcr.io/devcontainers/features/go:1": {}
|
||||
},
|
||||
// Configure tool-specific properties.
|
||||
"customizations": {
|
||||
// Configure properties specific to VS Code.
|
||||
"vscode": {
|
||||
// Set *default* container specific settings.json values on container create.
|
||||
"settings": {
|
||||
"go.toolsManagement.checkForUpdates": "local",
|
||||
"go.useLanguageServer": true,
|
||||
"go.gopath": "~/go",
|
||||
"[go]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": true
|
||||
}
|
||||
},
|
||||
"go.lintTool": "golangci-lint",
|
||||
"gopls": {
|
||||
"formatting.gofumpt": true,
|
||||
"usePlaceholders": false // add parameter placeholders when completing a function
|
||||
},
|
||||
"files.eol": "\n"
|
||||
},
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
"extensions": [
|
||||
"golang.Go"
|
||||
]
|
||||
}
|
||||
}
|
||||
// TODO: make this work.
|
||||
// "postStartCommand": "echo \"alias gr=\\\"go run /workspaces/lazydocker/main.go\\\"\" >> ~/.zhsrc"
|
||||
}
|
||||
20
.github/workflows/cd.yml
vendored
20
.github/workflows/cd.yml
vendored
|
|
@ -3,7 +3,7 @@ name: Continuous Delivery
|
|||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
|
|
@ -14,18 +14,14 @@ jobs:
|
|||
- name: Unshallow repo
|
||||
run: git fetch --prune --unshallow
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
go-version: 1.24.x
|
||||
- name: Run goreleaser
|
||||
uses: goreleaser/goreleaser-action@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GORELEASER_TOKEN}}
|
||||
homebrew:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Bump Homebrew formula
|
||||
uses: dawidd6/action-homebrew-bump-formula@v3
|
||||
with:
|
||||
token: ${{secrets.HOMEBREW_TOKEN}}
|
||||
formula: lazydocker
|
||||
distribution: goreleaser
|
||||
version: v1.17.2
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.TOKEN_GITHUB}}
|
||||
|
|
|
|||
26
.github/workflows/ci.yml
vendored
26
.github/workflows/ci.yml
vendored
|
|
@ -1,7 +1,7 @@
|
|||
name: Continuous Integration
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.18
|
||||
GO_VERSION: 1.24
|
||||
|
||||
on:
|
||||
push:
|
||||
|
|
@ -25,11 +25,11 @@ jobs:
|
|||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
go-version: 1.24.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
|
||||
|
|
@ -47,11 +47,11 @@ jobs:
|
|||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
go-version: 1.24.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-build
|
||||
|
|
@ -75,11 +75,11 @@ jobs:
|
|||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
go-version: 1.24.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
|
|
@ -102,11 +102,11 @@ jobs:
|
|||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
go-version: 1.24.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
|
||||
|
|
|
|||
25
.github/workflows/sponsors.yml
vendored
25
.github/workflows/sponsors.yml
vendored
|
|
@ -9,19 +9,20 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout 🛎️
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Generate Sponsors 💖
|
||||
uses: JamesIves/github-sponsors-readme-action@v1.0.8
|
||||
uses: JamesIves/github-sponsors-readme-action@v1.2.2
|
||||
with:
|
||||
token: ${{ secrets.SPONSORS_TOKEN }}
|
||||
file: 'README.md'
|
||||
token: ${{ secrets.TOKEN_GITHUB }}
|
||||
file: "README.md"
|
||||
if: ${{ github.repository == 'jesseduffield/lazydocker' }}
|
||||
|
||||
- name: Commit and push if changed
|
||||
run: |-
|
||||
git diff
|
||||
git config --global user.email "actions@users.noreply.github.com"
|
||||
git config --global user.name "README-bot"
|
||||
git add README.md
|
||||
git commit -m "Updated README.md" || exit 0
|
||||
git push
|
||||
- name: Create Pull Request 🚀
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
commit-message: "README.md: Update Sponsors"
|
||||
title: "README.md: Update Sponsors"
|
||||
author: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>"
|
||||
labels: "ignore-for-release"
|
||||
delete-branch: true
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@ lazydocker*
|
|||
TODO.md
|
||||
Lazydocker.code-workspace
|
||||
.vscode
|
||||
.idea
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
linters:
|
||||
disable:
|
||||
- structcheck # gives false positives
|
||||
enable:
|
||||
- gofumpt
|
||||
- thelper
|
||||
- goimports
|
||||
- tparallel
|
||||
- wastedassign
|
||||
- exportloopref
|
||||
- unparam
|
||||
- prealloc
|
||||
- unconvert
|
||||
|
|
@ -26,4 +23,5 @@ linters-settings:
|
|||
max-func-lines: 0
|
||||
|
||||
run:
|
||||
go: 1.18
|
||||
go: '1.21'
|
||||
timeout: 10m
|
||||
|
|
|
|||
5
CLAUDE.md
Normal file
5
CLAUDE.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# CLAUDE.md
|
||||
|
||||
## Build & test
|
||||
- All Go commands need `GOFLAGS=-mod=vendor` (deps are vendored, including the `jesseduffield/gocui` fork and the Docker SDK).
|
||||
- Unit tests: `GOFLAGS=-mod=vendor go test ./...`
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
ARG BASE_IMAGE_BUILDER=golang
|
||||
ARG ALPINE_VERSION=3.15
|
||||
ARG GO_VERSION=1.18
|
||||
ARG ALPINE_VERSION=3.20
|
||||
ARG GO_VERSION=1.23
|
||||
|
||||
FROM ${BASE_IMAGE_BUILDER}:${GO_VERSION}-alpine${ALPINE_VERSION} AS builder
|
||||
ARG GOARCH=amd64
|
||||
|
|
@ -19,7 +19,7 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=${GOARCH} GOARM=${GOARM} \
|
|||
FROM ${BASE_IMAGE_BUILDER}:${GO_VERSION}-alpine${ALPINE_VERSION} AS docker-builder
|
||||
ARG GOARCH=amd64
|
||||
ARG GOARM
|
||||
ARG DOCKER_VERSION=v20.10.13
|
||||
ARG DOCKER_VERSION=v27.0.3
|
||||
RUN apk add -U -q --progress --no-cache git bash coreutils gcc musl-dev
|
||||
WORKDIR /go/src/github.com/docker/cli
|
||||
RUN git clone --branch ${DOCKER_VERSION} --single-branch --depth 1 https://github.com/docker/cli.git . > /dev/null 2>&1
|
||||
|
|
|
|||
67
README.md
67
README.md
File diff suppressed because one or more lines are too long
BIN
assets/subble-job-ad.jpg
Normal file
BIN
assets/subble-job-ad.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 118 KiB |
BIN
assets/subble.webp
Normal file
BIN
assets/subble.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
BIN
assets/tuple.png
Normal file
BIN
assets/tuple.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 264 KiB |
BIN
assets/warp2.png
Normal file
BIN
assets/warp2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 359 KiB |
0
config/config.yml
Normal file
0
config/config.yml
Normal file
2170
coverage.txt
2170
coverage.txt
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,3 @@
|
|||
version: '3'
|
||||
services:
|
||||
lazydocker:
|
||||
build:
|
||||
|
|
|
|||
|
|
@ -10,20 +10,38 @@ Changes to the user config will only take place after closing and re-opening laz
|
|||
|
||||
- OSX: `~/Library/Application Support/jesseduffield/lazydocker/config.yml`
|
||||
- Linux: `~/.config/lazydocker/config.yml`
|
||||
- Windows: `C:\\Users\\<User>\\AppData\\Roaming\\jesseduffield\\lazydocker\\config.yml` (I think)
|
||||
- Windows: `C:\Users\<User>\AppData\Roaming\lazydocker\config.yml`
|
||||
|
||||
JSON schema is available for `config.yml` so that IntelliSense in Visual Studio Code
|
||||
(completion and error checking) is automatically enabled when the [YAML Red Hat][yaml]
|
||||
extension is installed. However, note that automatic schema detection only works
|
||||
if your config file is in one of the standard paths mentioned above. If you
|
||||
override the path to the file, you can still make IntelliSense work by adding
|
||||
|
||||
```yaml
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/lazydocker.json
|
||||
```
|
||||
|
||||
to the top of your config file or via [Visual Studio Code settings.json config][settings].
|
||||
|
||||
[yaml]: https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml
|
||||
[settings]: https://github.com/redhat-developer/vscode-yaml#associating-a-schema-to-a-glob-pattern-via-yamlschemas
|
||||
|
||||
## Default:
|
||||
|
||||
```yml
|
||||
gui:
|
||||
scrollHeight: 2
|
||||
language: 'auto' # one of 'auto' | 'en' | 'pl' | 'nl' | 'de' | 'tr'
|
||||
language: "auto" # one of 'auto' | 'en' | 'pl' | 'nl' | 'de' | 'tr'
|
||||
border: "rounded" # one of 'rounded' | 'single' | 'double' | 'hidden'
|
||||
theme:
|
||||
activeBorderColor:
|
||||
- green
|
||||
- bold
|
||||
inactiveBorderColor:
|
||||
- white
|
||||
selectedLineBgColor:
|
||||
- blue
|
||||
optionsTextColor:
|
||||
- blue
|
||||
returnImmediately: false
|
||||
|
|
@ -31,16 +49,23 @@ gui:
|
|||
# Side panel width as a ratio of the screen's width
|
||||
sidePanelWidth: 0.333
|
||||
# Determines whether we show the bottom line (the one containing keybinding
|
||||
# info and the status of the app).
|
||||
# info and the status of the app).
|
||||
showBottomLine: true
|
||||
# When true, increases vertical space used by focused side panel,
|
||||
# creating an accordion effect
|
||||
expandFocusedSidePanel: false
|
||||
# Determines which screen mode will be used on startup
|
||||
screenMode: "normal" # one of 'normal' | 'half' | 'fullscreen'
|
||||
# Determines the style of the container status and container health display in the
|
||||
# containers panel. "long": full words (default), "short": one or two characters,
|
||||
# "icon": unicode emoji.
|
||||
containerStatusHealthStyle: "long"
|
||||
logs:
|
||||
timestamps: false
|
||||
since: '60m'
|
||||
since: '60m' # set to '' to show all logs
|
||||
tail: '' # set to 200 to show last 200 lines of logs
|
||||
commandTemplates:
|
||||
dockerCompose: docker-compose
|
||||
dockerCompose: docker compose # Determines the Docker Compose command to run, referred to as .DockerCompose in commandTemplates
|
||||
restartService: '{{ .DockerCompose }} restart {{ .Service.Name }}'
|
||||
up: '{{ .DockerCompose }} up -d'
|
||||
down: '{{ .DockerCompose }} down'
|
||||
|
|
@ -103,6 +128,11 @@ customCommands:
|
|||
serviceNames: []
|
||||
```
|
||||
|
||||
You may use the following go templates (such as `{{ .Container.ID }}` above) in your commands:
|
||||
- `{{ .DockerCompose }}`: the docker compose command (default: `docker-compose`)
|
||||
- [`{{ .Container }}`](https://pkg.go.dev/github.com/jesseduffield/lazydocker@v0.20.0/pkg/commands#Container) and its fields. For example: `{{ .Container.Container.ImageID }}`
|
||||
- [`{{ .Service }}`](https://pkg.go.dev/github.com/jesseduffield/lazydocker@v0.20.0/pkg/commands#Service) and its fields. For example: `{{ .Service.Name }}`
|
||||
|
||||
## Replacements
|
||||
|
||||
You can add replacements like so:
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<kbd>enter</kbd>: fokussieren aufs Hauptpanel
|
||||
<kbd>[</kbd>: vorheriges Tab
|
||||
<kbd>]</kbd>: nächstes Tab
|
||||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Container
|
||||
|
|
@ -81,6 +82,18 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Netzwerk
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: führe vordefinierten benutzerdefinierten Befehl aus
|
||||
<kbd>d</kbd>: entferne Netzwerk
|
||||
<kbd>b</kbd>: view bulk commands
|
||||
<kbd>enter</kbd>: fokussieren aufs Hauptpanel
|
||||
<kbd>[</kbd>: vorheriges Tab
|
||||
<kbd>]</kbd>: nächstes Tab
|
||||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Haupt
|
||||
|
||||
<pre>
|
||||
|
|
@ -92,4 +105,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<pre>
|
||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: prev screen mode
|
||||
<kbd>1</kbd>: focus projects panel
|
||||
<kbd>2</kbd>: focus services panel
|
||||
<kbd>3</kbd>: focus containers panel
|
||||
<kbd>4</kbd>: focus images panel
|
||||
<kbd>5</kbd>: focus volumes panel
|
||||
<kbd>6</kbd>: focus networks panel
|
||||
</pre>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<kbd>enter</kbd>: focus main panel
|
||||
<kbd>[</kbd>: previous tab
|
||||
<kbd>]</kbd>: next tab
|
||||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Containers
|
||||
|
|
@ -81,6 +82,18 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Networks
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: run predefined custom command
|
||||
<kbd>d</kbd>: remove network
|
||||
<kbd>b</kbd>: view bulk commands
|
||||
<kbd>enter</kbd>: focus main panel
|
||||
<kbd>[</kbd>: previous tab
|
||||
<kbd>]</kbd>: next tab
|
||||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Main
|
||||
|
||||
<pre>
|
||||
|
|
@ -92,4 +105,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<pre>
|
||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: prev screen mode
|
||||
<kbd>1</kbd>: focus projects panel
|
||||
<kbd>2</kbd>: focus services panel
|
||||
<kbd>3</kbd>: focus containers panel
|
||||
<kbd>4</kbd>: focus images panel
|
||||
<kbd>5</kbd>: focus volumes panel
|
||||
<kbd>6</kbd>: focus networks panel
|
||||
</pre>
|
||||
|
|
|
|||
114
docs/keybindings/Keybindings_es.md
Normal file
114
docs/keybindings/Keybindings_es.md
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
|
||||
# Lazydocker menú
|
||||
|
||||
## Proyecto
|
||||
|
||||
<pre>
|
||||
<kbd>e</kbd>: editar configuración de lazydocker
|
||||
<kbd>o</kbd>: abrir configuración de lazydocker
|
||||
<kbd>m</kbd>: ver logs
|
||||
<kbd>enter</kbd>: enfocar panel principal
|
||||
<kbd>[</kbd>: anterior pestaña
|
||||
<kbd>]</kbd>: siguiente pestaña
|
||||
<kbd>/</kbd>: filtrar lista
|
||||
</pre>
|
||||
|
||||
## Contenedores
|
||||
|
||||
<pre>
|
||||
<kbd>d</kbd>: borrar
|
||||
<kbd>e</kbd>: esconder/mostrar contenedores parados
|
||||
<kbd>p</kbd>: pausa
|
||||
<kbd>s</kbd>: parar
|
||||
<kbd>r</kbd>: reiniciar
|
||||
<kbd>a</kbd>: attach
|
||||
<kbd>m</kbd>: ver logs
|
||||
<kbd>E</kbd>: ejecutar shell
|
||||
<kbd>c</kbd>: ejecutar comando personalizado
|
||||
<kbd>b</kbd>: ver comandos masivos
|
||||
<kbd>w</kbd>: abrir en navegador (first port is http)
|
||||
<kbd>enter</kbd>: enfocar panel principal
|
||||
<kbd>[</kbd>: anterior pestaña
|
||||
<kbd>]</kbd>: siguiente pestaña
|
||||
<kbd>/</kbd>: filtrar lista
|
||||
</pre>
|
||||
|
||||
## Servicios
|
||||
|
||||
<pre>
|
||||
<kbd>u</kbd>: levantar servicio
|
||||
<kbd>d</kbd>: borrar contenedores
|
||||
<kbd>s</kbd>: parar
|
||||
<kbd>p</kbd>: pausa
|
||||
<kbd>r</kbd>: reiniciar
|
||||
<kbd>S</kbd>: iniciar
|
||||
<kbd>a</kbd>: attach
|
||||
<kbd>m</kbd>: ver logs
|
||||
<kbd>U</kbd>: levantar proyecto
|
||||
<kbd>D</kbd>: dar de baja el proyecto
|
||||
<kbd>R</kbd>: ver opciones de reinicio
|
||||
<kbd>c</kbd>: ejecutar comando personalizado
|
||||
<kbd>b</kbd>: ver comandos masivos
|
||||
<kbd>E</kbd>: ejecutar shell
|
||||
<kbd>w</kbd>: abrir en navegador (first port is http)
|
||||
<kbd>enter</kbd>: enfocar panel principal
|
||||
<kbd>[</kbd>: anterior pestaña
|
||||
<kbd>]</kbd>: siguiente pestaña
|
||||
<kbd>/</kbd>: filtrar lista
|
||||
</pre>
|
||||
|
||||
## Imágenes
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: ejecutar comando personalizado
|
||||
<kbd>d</kbd>: limpiar imagen
|
||||
<kbd>b</kbd>: ver comandos masivos
|
||||
<kbd>enter</kbd>: enfocar panel principal
|
||||
<kbd>[</kbd>: anterior pestaña
|
||||
<kbd>]</kbd>: siguiente pestaña
|
||||
<kbd>/</kbd>: filtrar lista
|
||||
</pre>
|
||||
|
||||
## Volúmenes
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: ejecutar comando personalizado
|
||||
<kbd>d</kbd>: limpiar volúmen
|
||||
<kbd>b</kbd>: ver comandos masivos
|
||||
<kbd>enter</kbd>: enfocar panel principal
|
||||
<kbd>[</kbd>: anterior pestaña
|
||||
<kbd>]</kbd>: siguiente pestaña
|
||||
<kbd>/</kbd>: filtrar lista
|
||||
</pre>
|
||||
|
||||
## Redes
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: ejecutar comando personalizado
|
||||
<kbd>d</kbd>: limpiar red
|
||||
<kbd>b</kbd>: ver comandos masivos
|
||||
<kbd>enter</kbd>: enfocar panel principal
|
||||
<kbd>[</kbd>: anterior pestaña
|
||||
<kbd>]</kbd>: siguiente pestaña
|
||||
<kbd>/</kbd>: filtrar lista
|
||||
</pre>
|
||||
|
||||
## Inicio
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: regresar
|
||||
</pre>
|
||||
|
||||
## Global
|
||||
|
||||
<pre>
|
||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: prev screen mode
|
||||
<kbd>1</kbd>: focus projects panel
|
||||
<kbd>2</kbd>: focus services panel
|
||||
<kbd>3</kbd>: focus containers panel
|
||||
<kbd>4</kbd>: focus images panel
|
||||
<kbd>5</kbd>: focus volumes panel
|
||||
<kbd>6</kbd>: focus networks panel
|
||||
</pre>
|
||||
|
|
@ -6,11 +6,12 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
|
||||
<pre>
|
||||
<kbd>e</kbd>: modifier la configuration lazydocker
|
||||
<kbd>o</kbd>: ouvrire la configuration lazydocker
|
||||
<kbd>o</kbd>: ouvrir la configuration lazydocker
|
||||
<kbd>m</kbd>: voir les enregistrements
|
||||
<kbd>enter</kbd>: focus paneau principal
|
||||
<kbd>enter</kbd>: focus panneau principal
|
||||
<kbd>[</kbd>: onglet précédent
|
||||
<kbd>]</kbd>: onglet suivant
|
||||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Conteneurs
|
||||
|
|
@ -23,11 +24,11 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<kbd>r</kbd>: redémarrer
|
||||
<kbd>a</kbd>: attacher
|
||||
<kbd>m</kbd>: voir les enregistrements
|
||||
<kbd>E</kbd>: executer le shell
|
||||
<kbd>c</kbd>: executer une commande prédéfinie
|
||||
<kbd>b</kbd>: voir les commandes groupés
|
||||
<kbd>w</kbd>: ouvrir dans le navgateur (le premier port est http)
|
||||
<kbd>enter</kbd>: focus paneau principal
|
||||
<kbd>E</kbd>: exécuter le shell
|
||||
<kbd>c</kbd>: exécuter une commande prédéfinie
|
||||
<kbd>b</kbd>: voir les commandes groupées
|
||||
<kbd>w</kbd>: ouvrir dans le navigateur (le premier port est http)
|
||||
<kbd>enter</kbd>: focus panneau principal
|
||||
<kbd>[</kbd>: onglet précédent
|
||||
<kbd>]</kbd>: onglet suivant
|
||||
<kbd>/</kbd>: filter list
|
||||
|
|
@ -47,11 +48,11 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<kbd>U</kbd>: up project
|
||||
<kbd>D</kbd>: down project
|
||||
<kbd>R</kbd>: voir les options de redémarrage
|
||||
<kbd>c</kbd>: executer une commande prédéfinie
|
||||
<kbd>b</kbd>: voir les commandes groupés
|
||||
<kbd>E</kbd>: executer le shell
|
||||
<kbd>w</kbd>: ouvrir dans le navgateur (le premier port est http)
|
||||
<kbd>enter</kbd>: focus paneau principal
|
||||
<kbd>c</kbd>: exécuter une commande prédéfinie
|
||||
<kbd>b</kbd>: voir les commandes groupées
|
||||
<kbd>E</kbd>: exécuter le shell
|
||||
<kbd>w</kbd>: ouvrir dans le navigateur (le premier port est http)
|
||||
<kbd>enter</kbd>: focus panneau principal
|
||||
<kbd>[</kbd>: onglet précédent
|
||||
<kbd>]</kbd>: onglet suivant
|
||||
<kbd>/</kbd>: filter list
|
||||
|
|
@ -60,10 +61,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
## Images
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: executer une commande prédéfinie
|
||||
<kbd>c</kbd>: exécuter une commande prédéfinie
|
||||
<kbd>d</kbd>: supprimer l'image
|
||||
<kbd>b</kbd>: voir les commandes groupés
|
||||
<kbd>enter</kbd>: focus paneau principal
|
||||
<kbd>b</kbd>: voir les commandes groupées
|
||||
<kbd>enter</kbd>: focus panneau principal
|
||||
<kbd>[</kbd>: onglet précédent
|
||||
<kbd>]</kbd>: onglet suivant
|
||||
<kbd>/</kbd>: filter list
|
||||
|
|
@ -72,10 +73,22 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
## Volumes
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: executer une commande prédéfinie
|
||||
<kbd>c</kbd>: exécuter une commande prédéfinie
|
||||
<kbd>d</kbd>: supprimer le volume
|
||||
<kbd>b</kbd>: voir les commandes groupés
|
||||
<kbd>enter</kbd>: focus paneau principal
|
||||
<kbd>b</kbd>: voir les commandes groupées
|
||||
<kbd>enter</kbd>: focus panneau principal
|
||||
<kbd>[</kbd>: onglet précédent
|
||||
<kbd>]</kbd>: onglet suivant
|
||||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Réseaux
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: exécuter une commande prédéfinie
|
||||
<kbd>d</kbd>: supprimer le réseau
|
||||
<kbd>b</kbd>: voir les commandes groupées
|
||||
<kbd>enter</kbd>: focus panneau principal
|
||||
<kbd>[</kbd>: onglet précédent
|
||||
<kbd>]</kbd>: onglet suivant
|
||||
<kbd>/</kbd>: filter list
|
||||
|
|
@ -92,4 +105,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<pre>
|
||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: prev screen mode
|
||||
<kbd>1</kbd>: focus projects panel
|
||||
<kbd>2</kbd>: focus services panel
|
||||
<kbd>3</kbd>: focus containers panel
|
||||
<kbd>4</kbd>: focus images panel
|
||||
<kbd>5</kbd>: focus volumes panel
|
||||
<kbd>6</kbd>: focus networks panel
|
||||
</pre>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<kbd>enter</kbd>: focus hoofdpaneel
|
||||
<kbd>[</kbd>: vorige tab
|
||||
<kbd>]</kbd>: volgende tab
|
||||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Containers
|
||||
|
|
@ -81,6 +82,18 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Networks
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: draai een vooraf bedacht aangepaste opdracht
|
||||
<kbd>d</kbd>: verwijder network
|
||||
<kbd>b</kbd>: view bulk commands
|
||||
<kbd>enter</kbd>: focus hoofdpaneel
|
||||
<kbd>[</kbd>: vorige tab
|
||||
<kbd>]</kbd>: volgende tab
|
||||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Hoofd
|
||||
|
||||
<pre>
|
||||
|
|
@ -92,4 +105,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<pre>
|
||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: prev screen mode
|
||||
<kbd>1</kbd>: focus projects panel
|
||||
<kbd>2</kbd>: focus services panel
|
||||
<kbd>3</kbd>: focus containers panel
|
||||
<kbd>4</kbd>: focus images panel
|
||||
<kbd>5</kbd>: focus volumes panel
|
||||
<kbd>6</kbd>: focus networks panel
|
||||
</pre>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<kbd>enter</kbd>: skup na głównym panelu
|
||||
<kbd>[</kbd>: poprzednia zakładka
|
||||
<kbd>]</kbd>: następna zakładka
|
||||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Kontenery
|
||||
|
|
@ -81,6 +82,18 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Sieci
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: wykonaj predefiniowaną własną komende
|
||||
<kbd>d</kbd>: usuń sieci
|
||||
<kbd>b</kbd>: view bulk commands
|
||||
<kbd>enter</kbd>: skup na głównym panelu
|
||||
<kbd>[</kbd>: poprzednia zakładka
|
||||
<kbd>]</kbd>: następna zakładka
|
||||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Główne
|
||||
|
||||
<pre>
|
||||
|
|
@ -92,4 +105,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<pre>
|
||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: prev screen mode
|
||||
<kbd>1</kbd>: focus projects panel
|
||||
<kbd>2</kbd>: focus services panel
|
||||
<kbd>3</kbd>: focus containers panel
|
||||
<kbd>4</kbd>: focus images panel
|
||||
<kbd>5</kbd>: focus volumes panel
|
||||
<kbd>6</kbd>: focus networks panel
|
||||
</pre>
|
||||
|
|
|
|||
114
docs/keybindings/Keybindings_pt.md
Normal file
114
docs/keybindings/Keybindings_pt.md
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
|
||||
# Lazydocker menu
|
||||
|
||||
## Projeto
|
||||
|
||||
<pre>
|
||||
<kbd>e</kbd>: editar configuração do lazydocker
|
||||
<kbd>o</kbd>: abrir configuração do lazydocker
|
||||
<kbd>m</kbd>: ver logs
|
||||
<kbd>enter</kbd>: focar no painel principal
|
||||
<kbd>[</kbd>: aba anterior
|
||||
<kbd>]</kbd>: próxima aba
|
||||
<kbd>/</kbd>: filtrar lista
|
||||
</pre>
|
||||
|
||||
## Contêineres
|
||||
|
||||
<pre>
|
||||
<kbd>d</kbd>: remover
|
||||
<kbd>e</kbd>: ocultar/mostrar contêineres parados
|
||||
<kbd>p</kbd>: pausar
|
||||
<kbd>s</kbd>: parar
|
||||
<kbd>r</kbd>: reiniciar
|
||||
<kbd>a</kbd>: anexar
|
||||
<kbd>m</kbd>: ver logs
|
||||
<kbd>E</kbd>: executar shell
|
||||
<kbd>c</kbd>: executar comando personalizado predefinido
|
||||
<kbd>b</kbd>: ver comandos em massa
|
||||
<kbd>w</kbd>: abrir no navegador (primeira porta é http)
|
||||
<kbd>enter</kbd>: focar no painel principal
|
||||
<kbd>[</kbd>: aba anterior
|
||||
<kbd>]</kbd>: próxima aba
|
||||
<kbd>/</kbd>: filtrar lista
|
||||
</pre>
|
||||
|
||||
## Serviços
|
||||
|
||||
<pre>
|
||||
<kbd>u</kbd>: subir serviço
|
||||
<kbd>d</kbd>: remover contêineres
|
||||
<kbd>s</kbd>: parar
|
||||
<kbd>p</kbd>: pausar
|
||||
<kbd>r</kbd>: reiniciar
|
||||
<kbd>S</kbd>: iniciar
|
||||
<kbd>a</kbd>: anexar
|
||||
<kbd>m</kbd>: ver logs
|
||||
<kbd>U</kbd>: subir projeto
|
||||
<kbd>D</kbd>: derrubar projeto
|
||||
<kbd>R</kbd>: ver opções de reinício
|
||||
<kbd>c</kbd>: executar comando personalizado predefinido
|
||||
<kbd>b</kbd>: ver comandos em massa
|
||||
<kbd>E</kbd>: executar shell
|
||||
<kbd>w</kbd>: abrir no navegador (primeira porta é http)
|
||||
<kbd>enter</kbd>: focar no painel principal
|
||||
<kbd>[</kbd>: aba anterior
|
||||
<kbd>]</kbd>: próxima aba
|
||||
<kbd>/</kbd>: filtrar lista
|
||||
</pre>
|
||||
|
||||
## Imagens
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: executar comando personalizado predefinido
|
||||
<kbd>d</kbd>: remover imagem
|
||||
<kbd>b</kbd>: ver comandos em massa
|
||||
<kbd>enter</kbd>: focar no painel principal
|
||||
<kbd>[</kbd>: aba anterior
|
||||
<kbd>]</kbd>: próxima aba
|
||||
<kbd>/</kbd>: filtrar lista
|
||||
</pre>
|
||||
|
||||
## Volumes
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: executar comando personalizado predefinido
|
||||
<kbd>d</kbd>: remover volume
|
||||
<kbd>b</kbd>: ver comandos em massa
|
||||
<kbd>enter</kbd>: focar no painel principal
|
||||
<kbd>[</kbd>: aba anterior
|
||||
<kbd>]</kbd>: próxima aba
|
||||
<kbd>/</kbd>: filtrar lista
|
||||
</pre>
|
||||
|
||||
## Redes
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: executar comando personalizado predefinido
|
||||
<kbd>d</kbd>: remover rede
|
||||
<kbd>b</kbd>: ver comandos em massa
|
||||
<kbd>enter</kbd>: focar no painel principal
|
||||
<kbd>[</kbd>: aba anterior
|
||||
<kbd>]</kbd>: próxima aba
|
||||
<kbd>/</kbd>: filtrar lista
|
||||
</pre>
|
||||
|
||||
## Principal
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: retornar
|
||||
</pre>
|
||||
|
||||
## Global
|
||||
|
||||
<pre>
|
||||
<kbd>+</kbd>: modo de tela seguinte (normal/meia/tela cheia)
|
||||
<kbd>_</kbd>: modo de tela anterior
|
||||
<kbd>1</kbd>: focus projects panel
|
||||
<kbd>2</kbd>: focus services panel
|
||||
<kbd>3</kbd>: focus containers panel
|
||||
<kbd>4</kbd>: focus images panel
|
||||
<kbd>5</kbd>: focus volumes panel
|
||||
<kbd>6</kbd>: focus networks panel
|
||||
</pre>
|
||||
|
|
@ -11,6 +11,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<kbd>enter</kbd>: ana panele odaklan
|
||||
<kbd>[</kbd>: önceki sekme
|
||||
<kbd>]</kbd>: sonraki sekme
|
||||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Konteynerler
|
||||
|
|
@ -81,6 +82,18 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Ağları
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: önceden tanımlanmış özel komutu çalıştır
|
||||
<kbd>d</kbd>: ağı kaldır
|
||||
<kbd>b</kbd>: view bulk commands
|
||||
<kbd>enter</kbd>: ana panele odaklan
|
||||
<kbd>[</kbd>: önceki sekme
|
||||
<kbd>]</kbd>: sonraki sekme
|
||||
<kbd>/</kbd>: filter list
|
||||
</pre>
|
||||
|
||||
## Ana
|
||||
|
||||
<pre>
|
||||
|
|
@ -92,4 +105,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
<pre>
|
||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: prev screen mode
|
||||
<kbd>1</kbd>: focus projects panel
|
||||
<kbd>2</kbd>: focus services panel
|
||||
<kbd>3</kbd>: focus containers panel
|
||||
<kbd>4</kbd>: focus images panel
|
||||
<kbd>5</kbd>: focus volumes panel
|
||||
<kbd>6</kbd>: focus networks panel
|
||||
</pre>
|
||||
|
|
|
|||
114
docs/keybindings/Keybindings_zh.md
Normal file
114
docs/keybindings/Keybindings_zh.md
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
|
||||
# Lazydocker 菜单
|
||||
|
||||
## 项目
|
||||
|
||||
<pre>
|
||||
<kbd>e</kbd>: 编辑lazydocker配置
|
||||
<kbd>o</kbd>: 打开lazydocker配置
|
||||
<kbd>m</kbd>: 查看日志
|
||||
<kbd>enter</kbd>: 聚焦主面板
|
||||
<kbd>[</kbd>: 上一个选项卡
|
||||
<kbd>]</kbd>: 下一个选项卡
|
||||
<kbd>/</kbd>: 过滤列表
|
||||
</pre>
|
||||
|
||||
## 容器
|
||||
|
||||
<pre>
|
||||
<kbd>d</kbd>: 移除
|
||||
<kbd>e</kbd>: 隐藏/显示已停止的容器
|
||||
<kbd>p</kbd>: 暂停
|
||||
<kbd>s</kbd>: 停止
|
||||
<kbd>r</kbd>: 重新启动
|
||||
<kbd>a</kbd>: attach
|
||||
<kbd>m</kbd>: 查看日志
|
||||
<kbd>E</kbd>: 执行shell
|
||||
<kbd>c</kbd>: 运行预定义的自定义命令
|
||||
<kbd>b</kbd>: 查看批量命令
|
||||
<kbd>w</kbd>: 在浏览器中打开(第一个端口为http)
|
||||
<kbd>enter</kbd>: 聚焦主面板
|
||||
<kbd>[</kbd>: 上一个选项卡
|
||||
<kbd>]</kbd>: 下一个选项卡
|
||||
<kbd>/</kbd>: 过滤列表
|
||||
</pre>
|
||||
|
||||
## 服务
|
||||
|
||||
<pre>
|
||||
<kbd>u</kbd>: 启动服务
|
||||
<kbd>d</kbd>: 移除容器
|
||||
<kbd>s</kbd>: 停止
|
||||
<kbd>p</kbd>: 暂停
|
||||
<kbd>r</kbd>: 重新启动
|
||||
<kbd>S</kbd>: 启动项目
|
||||
<kbd>a</kbd>: attach
|
||||
<kbd>m</kbd>: 查看日志
|
||||
<kbd>U</kbd>: 创建并启动容器
|
||||
<kbd>D</kbd>: 停止并移除容器
|
||||
<kbd>R</kbd>: 查看重启选项
|
||||
<kbd>c</kbd>: 运行预定义的自定义命令
|
||||
<kbd>b</kbd>: 查看批量命令
|
||||
<kbd>E</kbd>: 执行shell
|
||||
<kbd>w</kbd>: 在浏览器中打开(第一个端口为http)
|
||||
<kbd>enter</kbd>: 聚焦主面板
|
||||
<kbd>[</kbd>: 上一个选项卡
|
||||
<kbd>]</kbd>: 下一个选项卡
|
||||
<kbd>/</kbd>: 过滤列表
|
||||
</pre>
|
||||
|
||||
## 镜像
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: 运行预定义的自定义命令
|
||||
<kbd>d</kbd>: 移除镜像
|
||||
<kbd>b</kbd>: 查看批量命令
|
||||
<kbd>enter</kbd>: 聚焦主面板
|
||||
<kbd>[</kbd>: 上一个选项卡
|
||||
<kbd>]</kbd>: 下一个选项卡
|
||||
<kbd>/</kbd>: 过滤列表
|
||||
</pre>
|
||||
|
||||
## 卷
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: 运行预定义的自定义命令
|
||||
<kbd>d</kbd>: 移除卷
|
||||
<kbd>b</kbd>: 查看批量命令
|
||||
<kbd>enter</kbd>: 聚焦主面板
|
||||
<kbd>[</kbd>: 上一个选项卡
|
||||
<kbd>]</kbd>: 下一个选项卡
|
||||
<kbd>/</kbd>: 过滤列表
|
||||
</pre>
|
||||
|
||||
## 网络
|
||||
|
||||
<pre>
|
||||
<kbd>c</kbd>: 运行预定义的自定义命令
|
||||
<kbd>d</kbd>: 移除网络
|
||||
<kbd>b</kbd>: 查看批量命令
|
||||
<kbd>enter</kbd>: 聚焦主面板
|
||||
<kbd>[</kbd>: 上一个选项卡
|
||||
<kbd>]</kbd>: 下一个选项卡
|
||||
<kbd>/</kbd>: 过滤列表
|
||||
</pre>
|
||||
|
||||
## 主要
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: 返回
|
||||
</pre>
|
||||
|
||||
## 全局
|
||||
|
||||
<pre>
|
||||
<kbd>+</kbd>: 下一个屏幕模式(正常/半屏/全屏)
|
||||
<kbd>_</kbd>: 上一个屏幕模式
|
||||
<kbd>1</kbd>: focus projects panel
|
||||
<kbd>2</kbd>: focus services panel
|
||||
<kbd>3</kbd>: focus containers panel
|
||||
<kbd>4</kbd>: focus images panel
|
||||
<kbd>5</kbd>: focus volumes panel
|
||||
<kbd>6</kbd>: focus networks panel
|
||||
</pre>
|
||||
85
go.mod
85
go.mod
|
|
@ -1,64 +1,85 @@
|
|||
module github.com/jesseduffield/lazydocker
|
||||
|
||||
go 1.18
|
||||
go 1.22
|
||||
|
||||
toolchain go1.23.6
|
||||
|
||||
require (
|
||||
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c
|
||||
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
||||
github.com/docker/docker v20.10.15+incompatible
|
||||
github.com/fatih/color v1.7.0
|
||||
github.com/go-errors/errors v1.4.2
|
||||
github.com/docker/cli v27.1.1+incompatible
|
||||
github.com/docker/docker v28.5.2+incompatible
|
||||
github.com/fatih/color v1.10.0
|
||||
github.com/go-errors/errors v1.5.1
|
||||
github.com/gookit/color v1.5.0
|
||||
github.com/imdario/mergo v0.3.8
|
||||
github.com/imdario/mergo v0.3.16
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20221023185936-ef06450f4fdc
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240418080333-8cd33929c513
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221023210126-718a4caea996
|
||||
github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56
|
||||
github.com/mattn/go-runewidth v0.0.14
|
||||
github.com/mattn/go-runewidth v0.0.15
|
||||
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767
|
||||
github.com/mgutz/str v1.2.0
|
||||
github.com/pmezard/go-difflib v1.0.0
|
||||
github.com/samber/lo v1.31.0
|
||||
github.com/sasha-s/go-deadlock v0.3.1
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/gdamore/encoding v1.0.0 // indirect
|
||||
github.com/gdamore/tcell/v2 v2.5.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.1 // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fvbommel/sortorder v1.1.0 // indirect
|
||||
github.com/gdamore/encoding v1.0.1 // indirect
|
||||
github.com/gdamore/tcell/v2 v2.7.4 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/goccy/go-yaml v1.11.0
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.4 // indirect
|
||||
github.com/mattn/go-isatty v0.0.11 // indirect
|
||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/onsi/ginkgo v1.8.0 // indirect
|
||||
github.com/onsi/gomega v1.5.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.2 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171 // indirect
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect
|
||||
golang.org/x/sys v0.1.0 // indirect
|
||||
golang.org/x/term v0.1.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
|
||||
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||
golang.org/x/crypto v0.24.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/term v0.21.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.2.0 // indirect
|
||||
gotest.tools/v3 v3.5.1 // indirect
|
||||
)
|
||||
|
|
|
|||
263
go.sum
263
go.sum
|
|
@ -1,194 +1,247 @@
|
|||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c h1:YDsGA6tou+tAxVe0Dre29iSbQ8TrWdWfwOisKArJT5E=
|
||||
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c/go.mod h1:tMoSueLQlMf0TCldjrJLNIjAc5qAOIcHt5REi88/Ygo=
|
||||
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1 h1:1fx+RA5lk1ZkzPAUP7DEgZnVHYxEcHO77vQO/V8z/2Q=
|
||||
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1/go.mod h1:z0nyIb42Zs97wyX1V+8MbEFhHeTw1OgFQfR6q57ZuHc=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v20.10.15+incompatible h1:dk9FewY/9Xwm4ay/HViEEHSQuM/kL4F+JaG6GQdgmGo=
|
||||
github.com/docker/docker v20.10.15+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE=
|
||||
github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
|
||||
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
|
||||
github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
|
||||
github.com/gdamore/tcell/v2 v2.5.3 h1:b9XQrT6QGbgI7JvZOJXFNczOQeIYbo8BfeSMzt2sAV0=
|
||||
github.com/gdamore/tcell/v2 v2.5.3/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
|
||||
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
|
||||
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
|
||||
github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU=
|
||||
github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg=
|
||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
||||
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54=
|
||||
github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw=
|
||||
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/integrii/flaggy v1.4.0 h1:A1x7SYx4jqu5NSrY14z8Z+0UyX2S5ygfJJrfolWR3zM=
|
||||
github.com/integrii/flaggy v1.4.0/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI=
|
||||
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee h1:7Zi/OQlGbMz4MT2V1+prN/gv1C64NDyVb/MbJnS0ZfA=
|
||||
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee/go.mod h1:Z9UKHveKXXgyo8ME7R8yxh/BUTFOK+FgfWKlhy8oOAg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20221023185936-ef06450f4fdc h1:Gi/uDpmlB5XbyAzqr0RUFtK+2bVpUKkdR4soj/JKU1c=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20221023185936-ef06450f4fdc/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240418080333-8cd33929c513 h1:Y1bw5iItrsDCumATc/rklIJ/6K+68ieiWZJedhrNuXo=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240418080333-8cd33929c513/go.mod h1:XtEbqCbn45keRXEu+OMZkjN5gw6AEob59afsgHjokZ8=
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221023210126-718a4caea996 h1:CH1en6GpXSwnXl5Ehc4WX1NpS3uw9qbi7o9A4T2YYmA=
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221023210126-718a4caea996/go.mod h1:qxN4mHOAyeIDLP7IK7defgPClM/z1Kze8VVQiaEjzsQ=
|
||||
github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56 h1:33wSxJWU/f2TAozHYtJ8zqBxEnEVYM+22moLoiAkxvg=
|
||||
github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56/go.mod h1:FZJBwOhE+RXz8EVZfY+xnbCw2cVOwxlK3/aIi581z/s=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767 h1:BrhJNdEFWGuiJk/3/SwsG5Rex3zjFxYsDi2bpd7382Y=
|
||||
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767/go.mod h1:ct+byCpkFokm4J0tiuAvB8cf2ttm6GcCe89Yr25nGKg=
|
||||
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
|
||||
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
|
||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
|
||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
|
||||
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/samber/lo v1.31.0 h1:Sfa+/064Tdo4SvlohQUQzBhgSer9v/coGvKQI/XLWAM=
|
||||
github.com/samber/lo v1.31.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8=
|
||||
github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
|
||||
github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
|
||||
github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
|
||||
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk=
|
||||
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||
go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
|
||||
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
|
||||
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171 h1:TfdoLivD44QwvssI9Sv1xwa5DcL5XQr4au4sZ2F2NV4=
|
||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
|
||||
google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I=
|
||||
gotest.tools/v3 v3.2.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
|
|
|
|||
4
main.go
4
main.go
|
|
@ -29,6 +29,7 @@ var (
|
|||
configFlag = false
|
||||
debuggingFlag = false
|
||||
composeFiles []string
|
||||
projectName string
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
@ -51,6 +52,7 @@ func main() {
|
|||
flaggy.Bool(&configFlag, "c", "config", "Print the current default config")
|
||||
flaggy.Bool(&debuggingFlag, "d", "debug", "a boolean")
|
||||
flaggy.StringSlice(&composeFiles, "f", "file", "Specify alternate compose files")
|
||||
flaggy.String(&projectName, "p", "project", "Specify a docker compose project name")
|
||||
flaggy.SetVersion(info)
|
||||
|
||||
flaggy.Parse()
|
||||
|
|
@ -71,7 +73,7 @@ func main() {
|
|||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
appConfig, err := config.NewAppConfig("lazydocker", version, commit, date, buildSource, debuggingFlag, composeFiles, projectDir)
|
||||
appConfig, err := config.NewAppConfig("lazydocker", version, commit, date, buildSource, debuggingFlag, composeFiles, projectDir, projectName)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ func Generate() {
|
|||
}
|
||||
|
||||
func generateAtDir(dir string) {
|
||||
mConfig, err := config.NewAppConfig("lazydocker", "", "", "", "", true, nil, "")
|
||||
mConfig, err := config.NewAppConfig("lazydocker", "", "", "", "", true, nil, "", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -96,6 +96,7 @@ func getBindingSections(mApp *app.App) []*bindingSection {
|
|||
"containers": mApp.Tr.ContainersTitle,
|
||||
"images": mApp.Tr.ImagesTitle,
|
||||
"volumes": mApp.Tr.VolumesTitle,
|
||||
"networks": mApp.Tr.NetworksTitle,
|
||||
}
|
||||
|
||||
bindingSections = addBinding(titleMap[viewName], bindingSections, binding)
|
||||
|
|
|
|||
|
|
@ -7,14 +7,12 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazydocker/pkg/i18n"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
|
@ -29,12 +27,12 @@ type Container struct {
|
|||
OneOff bool
|
||||
ProjectName string
|
||||
ID string
|
||||
Container dockerTypes.Container
|
||||
Container container.Summary
|
||||
Client *client.Client
|
||||
OSCommand *OSCommand
|
||||
Log *logrus.Entry
|
||||
StatHistory []*RecordedStats
|
||||
Details dockerTypes.ContainerJSON
|
||||
Details container.InspectResponse
|
||||
MonitoringStats bool
|
||||
DockerCommand LimitedDockerCommand
|
||||
Tr *i18n.TranslationSet
|
||||
|
|
@ -43,7 +41,7 @@ type Container struct {
|
|||
}
|
||||
|
||||
// Remove removes the container
|
||||
func (c *Container) Remove(options dockerTypes.ContainerRemoveOptions) error {
|
||||
func (c *Container) Remove(options container.RemoveOptions) error {
|
||||
c.Log.Warn(fmt.Sprintf("removing container %s", c.Name))
|
||||
if err := c.Client.ContainerRemove(context.Background(), c.ID, options); err != nil {
|
||||
if strings.Contains(err.Error(), "Stop the container before attempting removal or force remove") {
|
||||
|
|
@ -59,10 +57,16 @@ func (c *Container) Remove(options dockerTypes.ContainerRemoveOptions) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Start starts the container
|
||||
func (c *Container) Start() error {
|
||||
c.Log.Warn(fmt.Sprintf("starting container %s", c.Name))
|
||||
return c.Client.ContainerStart(context.Background(), c.ID, container.StartOptions{})
|
||||
}
|
||||
|
||||
// Stop stops the container
|
||||
func (c *Container) Stop() error {
|
||||
c.Log.Warn(fmt.Sprintf("stopping container %s", c.Name))
|
||||
return c.Client.ContainerStop(context.Background(), c.ID, nil)
|
||||
return c.Client.ContainerStop(context.Background(), c.ID, container.StopOptions{})
|
||||
}
|
||||
|
||||
// Pause pauses the container
|
||||
|
|
@ -80,7 +84,7 @@ func (c *Container) Unpause() error {
|
|||
// Restart restarts the container
|
||||
func (c *Container) Restart() error {
|
||||
c.Log.Warn(fmt.Sprintf("restarting container %s", c.Name))
|
||||
return c.Client.ContainerRestart(context.Background(), c.ID, nil)
|
||||
return c.Client.ContainerRestart(context.Background(), c.ID, container.StopOptions{})
|
||||
}
|
||||
|
||||
// Attach attaches the container
|
||||
|
|
@ -100,20 +104,20 @@ func (c *Container) Attach() (*exec.Cmd, error) {
|
|||
|
||||
c.Log.Warn(fmt.Sprintf("attaching to container %s", c.Name))
|
||||
// TODO: use SDK
|
||||
cmd := c.OSCommand.PrepareSubProcess("docker", "attach", "--sig-proxy=false", c.ID)
|
||||
cmd := c.OSCommand.NewCmd("docker", "attach", "--sig-proxy=false", c.ID)
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
// Top returns process information
|
||||
func (c *Container) Top(ctx context.Context) (container.ContainerTopOKBody, error) {
|
||||
func (c *Container) Top(ctx context.Context) (container.TopResponse, error) {
|
||||
detail, err := c.Inspect()
|
||||
if err != nil {
|
||||
return container.ContainerTopOKBody{}, err
|
||||
return container.TopResponse{}, err
|
||||
}
|
||||
|
||||
// check container status
|
||||
if !detail.State.Running {
|
||||
return container.ContainerTopOKBody{}, errors.New("container is not running")
|
||||
return container.TopResponse{}, errors.New("container is not running")
|
||||
}
|
||||
|
||||
return c.Client.ContainerTop(ctx, c.ID, []string{})
|
||||
|
|
@ -126,7 +130,7 @@ func (c *DockerCommand) PruneContainers() error {
|
|||
}
|
||||
|
||||
// Inspect returns details about the container
|
||||
func (c *Container) Inspect() (dockerTypes.ContainerJSON, error) {
|
||||
func (c *Container) Inspect() (container.InspectResponse, error) {
|
||||
return c.Client.ContainerInspect(context.Background(), c.ID)
|
||||
}
|
||||
|
||||
|
|
@ -140,7 +144,9 @@ func (c *Container) RenderTop(ctx context.Context) (string, error) {
|
|||
return utils.RenderTable(append([][]string{result.Titles}, result.Processes...))
|
||||
}
|
||||
|
||||
// DetailsLoaded tells us whether we have yet loaded the details for a container. Because this is an asynchronous operation, sometimes we have the container before we have its details.
|
||||
// DetailsLoaded tells us whether we have yet loaded the details for a container.
|
||||
// Sometimes it takes some time for a container to have its details loaded
|
||||
// after it starts.
|
||||
func (c *Container) DetailsLoaded() bool {
|
||||
return c.Details.ContainerJSONBase != nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,18 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
ogLog "log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
cliconfig "github.com/docker/cli/cli/config"
|
||||
ddocker "github.com/docker/cli/cli/context/docker"
|
||||
ctxstore "github.com/docker/cli/cli/context/store"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands/ssh"
|
||||
|
|
@ -23,7 +30,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
APIVersion = "1.25"
|
||||
dockerHostEnvKey = "DOCKER_HOST"
|
||||
)
|
||||
|
||||
// DockerCommand is our main docker interface
|
||||
|
|
@ -34,9 +41,11 @@ type DockerCommand struct {
|
|||
Config *config.AppConfig
|
||||
Client *client.Client
|
||||
InDockerComposeProject bool
|
||||
ErrorChan chan error
|
||||
ContainerMutex deadlock.Mutex
|
||||
ServiceMutex deadlock.Mutex
|
||||
// LocalProjectName is the compose project name for the directory where lazydocker was launched.
|
||||
LocalProjectName string
|
||||
ErrorChan chan error
|
||||
ContainerMutex deadlock.Mutex
|
||||
ServiceMutex deadlock.Mutex
|
||||
|
||||
Closers []io.Closer
|
||||
}
|
||||
|
|
@ -55,23 +64,68 @@ type CommandObject struct {
|
|||
Container *Container
|
||||
Image *Image
|
||||
Volume *Volume
|
||||
Network *Network
|
||||
Project *Project
|
||||
}
|
||||
|
||||
// NewCommandObject takes a command object and returns a default command object with the passed command object merged in
|
||||
func (c *DockerCommand) NewCommandObject(obj CommandObject) CommandObject {
|
||||
defaultObj := CommandObject{DockerCompose: c.Config.UserConfig.CommandTemplates.DockerCompose}
|
||||
_ = mergo.Merge(&defaultObj, obj)
|
||||
|
||||
// When operating on a specific project, include -p flag so that
|
||||
// docker compose targets the correct project.
|
||||
if obj.Service != nil && obj.Service.ProjectName != "" {
|
||||
defaultObj.DockerCompose = fmt.Sprintf("%s -p %s", defaultObj.DockerCompose, obj.Service.ProjectName)
|
||||
} else if obj.Project != nil && obj.Project.Name != "" {
|
||||
defaultObj.DockerCompose = fmt.Sprintf("%s -p %s", defaultObj.DockerCompose, obj.Project.Name)
|
||||
}
|
||||
|
||||
return defaultObj
|
||||
}
|
||||
|
||||
// newDockerClient creates a Docker client with the given host.
|
||||
// We avoid using client.FromEnv because it includes WithVersionFromEnv() which
|
||||
// sets manualOverride=true when DOCKER_API_VERSION is set, preventing API version
|
||||
// negotiation even when WithAPIVersionNegotiation() is specified.
|
||||
// Instead, we explicitly configure only what we need, and rely on proper
|
||||
// API version negotiation to support older Docker daemons.
|
||||
// See https://github.com/jesseduffield/lazydocker/issues/715
|
||||
func newDockerClient(dockerHost string) (*client.Client, error) {
|
||||
return client.NewClientWithOpts(
|
||||
client.WithTLSClientConfigFromEnv(),
|
||||
client.WithAPIVersionNegotiation(),
|
||||
client.WithHost(dockerHost),
|
||||
)
|
||||
}
|
||||
|
||||
// NewDockerCommand it runs docker commands
|
||||
func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*DockerCommand, error) {
|
||||
dockerHost, err := determineDockerHost()
|
||||
if err != nil {
|
||||
ogLog.Printf("> could not determine host %v", err)
|
||||
}
|
||||
|
||||
// NOTE: Inject the determined docker host to the environment. This allows the
|
||||
// `SSHHandler.HandleSSHDockerHost()` to create a local unix socket tunneled
|
||||
// over SSH to the specified ssh host.
|
||||
if strings.HasPrefix(dockerHost, "ssh://") {
|
||||
os.Setenv(dockerHostEnvKey, dockerHost)
|
||||
}
|
||||
|
||||
tunnelCloser, err := ssh.NewSSHHandler(osCommand).HandleSSHDockerHost()
|
||||
if err != nil {
|
||||
ogLog.Fatal(err)
|
||||
}
|
||||
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion(APIVersion))
|
||||
// Retrieve the docker host from the environment which could have been set by
|
||||
// the `SSHHandler.HandleSSHDockerHost()` and override `dockerHost`.
|
||||
dockerHostFromEnv := os.Getenv(dockerHostEnvKey)
|
||||
if dockerHostFromEnv != "" {
|
||||
dockerHost = dockerHostFromEnv
|
||||
}
|
||||
|
||||
cli, err := newDockerClient(dockerHost)
|
||||
if err != nil {
|
||||
ogLog.Fatal(err)
|
||||
}
|
||||
|
|
@ -87,12 +141,7 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Translat
|
|||
Closers: []io.Closer{tunnelCloser},
|
||||
}
|
||||
|
||||
command := utils.ApplyTemplate(
|
||||
config.UserConfig.CommandTemplates.CheckDockerComposeConfig,
|
||||
dockerCommand.NewCommandObject(CommandObject{}),
|
||||
)
|
||||
|
||||
log.Warn(command)
|
||||
dockerCommand.setDockerComposeCommand(config)
|
||||
|
||||
err = osCommand.RunCommand(
|
||||
utils.ApplyTemplate(
|
||||
|
|
@ -105,9 +154,37 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Translat
|
|||
log.Warn(err.Error())
|
||||
}
|
||||
|
||||
// When the user passes -p outside of a compose directory, treat it as the
|
||||
// local project so the project/services panels still appear and filtering
|
||||
// is applied. Inside a compose dir, LocalProjectName is derived from
|
||||
// container labels later in RefreshContainersAndServices.
|
||||
if !dockerCommand.InDockerComposeProject && config.ProjectName != "" {
|
||||
dockerCommand.LocalProjectName = config.ProjectName
|
||||
}
|
||||
|
||||
return dockerCommand, nil
|
||||
}
|
||||
|
||||
// IsProjectScoped reports whether lazydocker should be scoped to a single
|
||||
// compose project — either because we're inside a compose directory or
|
||||
// because the user passed -p. When false, the project/services panels are
|
||||
// hidden and all containers are shown in a flat list.
|
||||
func (c *DockerCommand) IsProjectScoped() bool {
|
||||
return c.InDockerComposeProject || c.Config.ProjectName != ""
|
||||
}
|
||||
|
||||
func (c *DockerCommand) setDockerComposeCommand(config *config.AppConfig) {
|
||||
if config.UserConfig.CommandTemplates.DockerCompose != "docker compose" {
|
||||
return
|
||||
}
|
||||
|
||||
// it's possible that a user is still using docker-compose, so we'll check if 'docker comopose' is available, and if not, we'll fall back to 'docker-compose'
|
||||
err := c.OSCommand.RunCommand("docker compose version")
|
||||
if err != nil {
|
||||
config.UserConfig.CommandTemplates.DockerCompose = "docker-compose"
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DockerCommand) Close() error {
|
||||
return utils.CloseMany(c.Closers)
|
||||
}
|
||||
|
|
@ -146,7 +223,7 @@ func (c *DockerCommand) CreateClientStatMonitor(container *Container) {
|
|||
container.MonitoringStats = false
|
||||
}
|
||||
|
||||
func (c *DockerCommand) RefreshContainersAndServices(currentServices []*Service, currentContainers []*Container) ([]*Container, []*Service, error) {
|
||||
func (c *DockerCommand) RefreshContainersAndServices(currentContainers []*Container) ([]*Container, []*Service, error) {
|
||||
c.ServiceMutex.Lock()
|
||||
defer c.ServiceMutex.Unlock()
|
||||
|
||||
|
|
@ -155,28 +232,137 @@ func (c *DockerCommand) RefreshContainersAndServices(currentServices []*Service,
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
var services []*Service
|
||||
// we only need to get these services once because they won't change in the runtime of the program
|
||||
if currentServices != nil {
|
||||
services = currentServices
|
||||
} else {
|
||||
services, err = c.GetServices()
|
||||
// Derive services from container labels (covers all projects)
|
||||
services := c.GetServicesFromContainers(containers)
|
||||
|
||||
var composeServices []*Service
|
||||
if c.InDockerComposeProject {
|
||||
composeServices, err = c.GetServices()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
c.Log.Warn("Failed to get compose services: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the local project name before merging services, since
|
||||
// mergeServices needs it. We match compose service names against container
|
||||
// labels to handle cases where the project name differs from the directory
|
||||
// name (e.g. a `name:` directive in the compose file).
|
||||
if c.LocalProjectName == "" && c.InDockerComposeProject && composeServices != nil {
|
||||
for _, ctr := range containers {
|
||||
if ctr.ProjectName == "" || ctr.ServiceName == "" {
|
||||
continue
|
||||
}
|
||||
for _, svc := range composeServices {
|
||||
if ctr.ServiceName == svc.Name {
|
||||
c.LocalProjectName = ctr.ProjectName
|
||||
break
|
||||
}
|
||||
}
|
||||
if c.LocalProjectName != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Fall back to directory name
|
||||
if c.LocalProjectName == "" && c.Config.ProjectDir != "" {
|
||||
c.LocalProjectName = filepath.Base(c.Config.ProjectDir)
|
||||
}
|
||||
}
|
||||
|
||||
// Merge compose services (which include stopped services) with
|
||||
// container-derived services from all projects
|
||||
if composeServices != nil {
|
||||
services = c.mergeServices(services, composeServices)
|
||||
}
|
||||
|
||||
c.assignContainersToServices(containers, services)
|
||||
|
||||
return containers, services, nil
|
||||
}
|
||||
|
||||
// GetServicesFromContainers derives services from container labels for all projects
|
||||
func (c *DockerCommand) GetServicesFromContainers(containers []*Container) []*Service {
|
||||
// Use project+service as key to avoid duplicates
|
||||
type serviceKey struct {
|
||||
project string
|
||||
service string
|
||||
}
|
||||
seen := make(map[serviceKey]bool)
|
||||
services := make([]*Service, 0, len(containers))
|
||||
|
||||
for _, ctr := range containers {
|
||||
if ctr.ServiceName == "" || ctr.OneOff {
|
||||
continue
|
||||
}
|
||||
key := serviceKey{project: ctr.ProjectName, service: ctr.ServiceName}
|
||||
if seen[key] {
|
||||
continue
|
||||
}
|
||||
seen[key] = true
|
||||
services = append(services, &Service{
|
||||
Name: ctr.ServiceName,
|
||||
ID: ctr.ProjectName + "-" + ctr.ServiceName,
|
||||
ProjectName: ctr.ProjectName,
|
||||
OSCommand: c.OSCommand,
|
||||
Log: c.Log,
|
||||
DockerCommand: c,
|
||||
})
|
||||
}
|
||||
|
||||
return services
|
||||
}
|
||||
|
||||
// mergeServices merges compose services (which may lack ProjectName) with
|
||||
// container-derived services. Compose services take priority because they
|
||||
// include services without running containers.
|
||||
func (c *DockerCommand) mergeServices(containerServices []*Service, composeServices []*Service) []*Service {
|
||||
// Set project name on compose services
|
||||
for _, svc := range composeServices {
|
||||
if svc.ProjectName == "" {
|
||||
svc.ProjectName = c.LocalProjectName
|
||||
}
|
||||
}
|
||||
|
||||
// Build a set of compose service names for the local project
|
||||
composeServiceNames := make(map[string]bool)
|
||||
for _, svc := range composeServices {
|
||||
composeServiceNames[svc.Name] = true
|
||||
}
|
||||
|
||||
// Start with compose services, then add container-derived services
|
||||
// that aren't already covered by compose (i.e. from other projects)
|
||||
result := make([]*Service, 0, len(composeServices)+len(containerServices))
|
||||
result = append(result, composeServices...)
|
||||
|
||||
for _, svc := range containerServices {
|
||||
if svc.ProjectName == c.LocalProjectName && composeServiceNames[svc.Name] {
|
||||
continue // already covered by compose service
|
||||
}
|
||||
result = append(result, svc)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GetProjectNames returns all unique project names from containers
|
||||
func (c *DockerCommand) GetProjectNames(containers []*Container) []string {
|
||||
seen := make(map[string]bool)
|
||||
var names []string
|
||||
for _, ctr := range containers {
|
||||
if ctr.ProjectName != "" && !seen[ctr.ProjectName] {
|
||||
seen[ctr.ProjectName] = true
|
||||
names = append(names, ctr.ProjectName)
|
||||
}
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
||||
func (c *DockerCommand) assignContainersToServices(containers []*Container, services []*Service) {
|
||||
L:
|
||||
for _, service := range services {
|
||||
for _, container := range containers {
|
||||
if !container.OneOff && container.ServiceName == service.Name {
|
||||
service.Container = container
|
||||
for _, ctr := range containers {
|
||||
if !ctr.OneOff && ctr.ServiceName == service.Name && ctr.ProjectName == service.ProjectName {
|
||||
service.Container = ctr
|
||||
continue L
|
||||
}
|
||||
}
|
||||
|
|
@ -189,19 +375,19 @@ func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Conta
|
|||
c.ContainerMutex.Lock()
|
||||
defer c.ContainerMutex.Unlock()
|
||||
|
||||
containers, err := c.Client.ContainerList(context.Background(), dockerTypes.ContainerListOptions{All: true})
|
||||
containers, err := c.Client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ownContainers := make([]*Container, len(containers))
|
||||
|
||||
for i, container := range containers {
|
||||
for i, ctr := range containers {
|
||||
var newContainer *Container
|
||||
|
||||
// check if we already data stored against the container
|
||||
// check if we already have data stored against the container
|
||||
for _, existingContainer := range existingContainers {
|
||||
if existingContainer.ID == container.ID {
|
||||
if existingContainer.ID == ctr.ID {
|
||||
newContainer = existingContainer
|
||||
break
|
||||
}
|
||||
|
|
@ -210,7 +396,7 @@ func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Conta
|
|||
// initialise the container if it's completely new
|
||||
if newContainer == nil {
|
||||
newContainer = &Container{
|
||||
ID: container.ID,
|
||||
ID: ctr.ID,
|
||||
Client: c.Client,
|
||||
OSCommand: c.OSCommand,
|
||||
Log: c.Log,
|
||||
|
|
@ -219,21 +405,27 @@ func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Conta
|
|||
}
|
||||
}
|
||||
|
||||
newContainer.Container = container
|
||||
newContainer.Container = ctr
|
||||
// if the container is made with a name label we will use that
|
||||
if name, ok := container.Labels["name"]; ok {
|
||||
if name, ok := ctr.Labels["name"]; ok {
|
||||
newContainer.Name = name
|
||||
} else {
|
||||
newContainer.Name = strings.TrimLeft(container.Names[0], "/")
|
||||
if len(ctr.Names) > 0 {
|
||||
newContainer.Name = strings.TrimLeft(ctr.Names[0], "/")
|
||||
} else {
|
||||
newContainer.Name = ctr.ID
|
||||
}
|
||||
}
|
||||
newContainer.ServiceName = container.Labels["com.docker.compose.service"]
|
||||
newContainer.ProjectName = container.Labels["com.docker.compose.project"]
|
||||
newContainer.ContainerNumber = container.Labels["com.docker.compose.container"]
|
||||
newContainer.OneOff = container.Labels["com.docker.compose.oneoff"] == "True"
|
||||
newContainer.ServiceName = ctr.Labels["com.docker.compose.service"]
|
||||
newContainer.ProjectName = ctr.Labels["com.docker.compose.project"]
|
||||
newContainer.ContainerNumber = ctr.Labels["com.docker.compose.container"]
|
||||
newContainer.OneOff = ctr.Labels["com.docker.compose.oneoff"] == "True"
|
||||
|
||||
ownContainers[i] = newContainer
|
||||
}
|
||||
|
||||
c.SetContainerDetails(ownContainers)
|
||||
|
||||
return ownContainers, nil
|
||||
}
|
||||
|
||||
|
|
@ -244,22 +436,22 @@ func (c *DockerCommand) GetServices() ([]*Service, error) {
|
|||
}
|
||||
|
||||
composeCommand := c.Config.UserConfig.CommandTemplates.DockerCompose
|
||||
output, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("%s config --hash=*", composeCommand))
|
||||
output, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("%s config --services", composeCommand))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// output looks like:
|
||||
// service1 998d6d286b0499e0ff23d66302e720991a2asdkf9c30d0542034f610daf8a971
|
||||
// service2 asdld98asdklasd9bccd02438de0994f8e19cbe691feb3755336ec5ca2c55971
|
||||
// service1
|
||||
// service2
|
||||
|
||||
lines := utils.SplitLines(output)
|
||||
services := make([]*Service, len(lines))
|
||||
for i, str := range lines {
|
||||
arr := strings.Split(str, " ")
|
||||
services[i] = &Service{
|
||||
Name: arr[0],
|
||||
ID: arr[1],
|
||||
Name: str,
|
||||
ID: c.LocalProjectName + "-" + str,
|
||||
ProjectName: c.LocalProjectName,
|
||||
OSCommand: c.OSCommand,
|
||||
Log: c.Log,
|
||||
DockerCommand: c,
|
||||
|
|
@ -269,30 +461,41 @@ func (c *DockerCommand) GetServices() ([]*Service, error) {
|
|||
return services, nil
|
||||
}
|
||||
|
||||
// UpdateContainerDetails attaches the details returned from docker inspect to each of the containers
|
||||
// this contains a bit more info than what you get from the go-docker client
|
||||
func (c *DockerCommand) UpdateContainerDetails(containers []*Container) error {
|
||||
func (c *DockerCommand) RefreshContainerDetails(containers []*Container) error {
|
||||
c.ContainerMutex.Lock()
|
||||
defer c.ContainerMutex.Unlock()
|
||||
|
||||
for _, container := range containers {
|
||||
details, err := c.Client.ContainerInspect(context.Background(), container.ID)
|
||||
if err != nil {
|
||||
c.Log.Error(err)
|
||||
} else {
|
||||
container.Details = details
|
||||
}
|
||||
}
|
||||
c.SetContainerDetails(containers)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Attaches the details returned from docker inspect to each of the containers
|
||||
// this contains a bit more info than what you get from the go-docker client
|
||||
func (c *DockerCommand) SetContainerDetails(containers []*Container) {
|
||||
wg := sync.WaitGroup{}
|
||||
for _, ctr := range containers {
|
||||
ctr := ctr
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
details, err := c.Client.ContainerInspect(context.Background(), ctr.ID)
|
||||
if err != nil {
|
||||
c.Log.Error(err)
|
||||
} else {
|
||||
ctr.Details = details
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// ViewAllLogs attaches to a subprocess viewing all the logs from docker-compose
|
||||
func (c *DockerCommand) ViewAllLogs() (*exec.Cmd, error) {
|
||||
func (c *DockerCommand) ViewAllLogs(project *Project) (*exec.Cmd, error) {
|
||||
cmd := c.OSCommand.ExecutableFromString(
|
||||
utils.ApplyTemplate(
|
||||
c.OSCommand.Config.UserConfig.CommandTemplates.ViewAllLogs,
|
||||
c.NewCommandObject(CommandObject{}),
|
||||
c.NewCommandObject(CommandObject{Project: project}),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -303,10 +506,15 @@ func (c *DockerCommand) ViewAllLogs() (*exec.Cmd, error) {
|
|||
|
||||
// DockerComposeConfig returns the result of 'docker-compose config'
|
||||
func (c *DockerCommand) DockerComposeConfig() string {
|
||||
return c.DockerComposeConfigForProject(nil)
|
||||
}
|
||||
|
||||
// DockerComposeConfigForProject returns the result of 'docker-compose config' for a specific project
|
||||
func (c *DockerCommand) DockerComposeConfigForProject(project *Project) string {
|
||||
output, err := c.OSCommand.RunCommandWithOutput(
|
||||
utils.ApplyTemplate(
|
||||
c.OSCommand.Config.UserConfig.CommandTemplates.DockerComposeConfig,
|
||||
c.NewCommandObject(CommandObject{}),
|
||||
c.NewCommandObject(CommandObject{Project: project}),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -314,3 +522,64 @@ func (c *DockerCommand) DockerComposeConfig() string {
|
|||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// determineDockerHost tries to the determine the docker host that we should connect to
|
||||
// in the following order of decreasing precedence:
|
||||
// - value of "DOCKER_HOST" environment variable
|
||||
// - host retrieved from the current context (specified via DOCKER_CONTEXT)
|
||||
// - "default docker host" for the host operating system, otherwise
|
||||
func determineDockerHost() (string, error) {
|
||||
// If the docker host is explicitly set via the "DOCKER_HOST" environment variable,
|
||||
// then its a no-brainer :shrug:
|
||||
if os.Getenv("DOCKER_HOST") != "" {
|
||||
return os.Getenv("DOCKER_HOST"), nil
|
||||
}
|
||||
|
||||
currentContext := os.Getenv("DOCKER_CONTEXT")
|
||||
if currentContext == "" {
|
||||
cf, err := cliconfig.Load(cliconfig.Dir())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
currentContext = cf.CurrentContext
|
||||
}
|
||||
|
||||
// On some systems (windows) `default` is stored in the docker config as the currentContext.
|
||||
if currentContext == "" || currentContext == "default" {
|
||||
// If a docker context is neither specified via the "DOCKER_CONTEXT" environment variable nor via the
|
||||
// $HOME/.docker/config file, then we fall back to connecting to the "default docker host" meant for
|
||||
// the host operating system.
|
||||
return defaultDockerHost, nil
|
||||
}
|
||||
|
||||
storeConfig := ctxstore.NewConfig(
|
||||
func() interface{} { return &ddocker.EndpointMeta{} },
|
||||
ctxstore.EndpointTypeGetter(ddocker.DockerEndpoint, func() interface{} { return &ddocker.EndpointMeta{} }),
|
||||
)
|
||||
|
||||
st := ctxstore.New(cliconfig.ContextStoreDir(), storeConfig)
|
||||
md, err := st.GetMetadata(currentContext)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dockerEP, ok := md.Endpoints[ddocker.DockerEndpoint]
|
||||
if !ok {
|
||||
return "", err
|
||||
}
|
||||
dockerEPMeta, ok := dockerEP.(ddocker.EndpointMeta)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("expected docker.EndpointMeta, got %T", dockerEP)
|
||||
}
|
||||
|
||||
if dockerEPMeta.Host != "" {
|
||||
return dockerEPMeta.Host, nil
|
||||
}
|
||||
|
||||
// We might end up here, if the context was created with the `host` set to an empty value (i.e. '').
|
||||
// For example:
|
||||
// ```sh
|
||||
// docker context create foo --docker "host="
|
||||
// ```
|
||||
// In such scenario, we mimic the `docker` cli and try to connect to the "default docker host".
|
||||
return defaultDockerHost, nil
|
||||
}
|
||||
|
|
|
|||
7
pkg/commands/docker_host_unix.go
Normal file
7
pkg/commands/docker_host_unix.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
//go:build !windows
|
||||
|
||||
package commands
|
||||
|
||||
const (
|
||||
defaultDockerHost = "unix:///var/run/docker.sock"
|
||||
)
|
||||
5
pkg/commands/docker_host_windows.go
Normal file
5
pkg/commands/docker_host_windows.go
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
package commands
|
||||
|
||||
const (
|
||||
defaultDockerHost = "npipe:////./pipe/docker_engine"
|
||||
)
|
||||
91
pkg/commands/docker_test.go
Normal file
91
pkg/commands/docker_test.go
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TestNewDockerClientVersionNegotiation verifies that newDockerClient allows
|
||||
// API version negotiation even when DOCKER_API_VERSION is set.
|
||||
//
|
||||
// This is a regression test for https://github.com/jesseduffield/lazydocker/issues/715
|
||||
// where users got "client version 1.25 is too old" errors because FromEnv()
|
||||
// includes WithVersionFromEnv() which sets manualOverride=true, preventing
|
||||
// API version negotiation.
|
||||
func TestNewDockerClientVersionNegotiation(t *testing.T) {
|
||||
// Save original env var and restore after test
|
||||
originalAPIVersion := os.Getenv("DOCKER_API_VERSION")
|
||||
defer func() {
|
||||
if originalAPIVersion == "" {
|
||||
os.Unsetenv("DOCKER_API_VERSION")
|
||||
} else {
|
||||
os.Setenv("DOCKER_API_VERSION", originalAPIVersion)
|
||||
}
|
||||
}()
|
||||
|
||||
// Set DOCKER_API_VERSION to an old version that would cause
|
||||
// "client version 1.25 is too old" errors if negotiation is disabled
|
||||
os.Setenv("DOCKER_API_VERSION", "1.25")
|
||||
|
||||
t.Run("FromEnv locks version preventing negotiation", func(t *testing.T) {
|
||||
// This demonstrates the problematic behavior we're avoiding.
|
||||
// When using FromEnv with DOCKER_API_VERSION set, the client
|
||||
// version gets locked to 1.25 and negotiation is disabled.
|
||||
cli, err := client.NewClientWithOpts(
|
||||
client.FromEnv,
|
||||
client.WithAPIVersionNegotiation(),
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
defer cli.Close()
|
||||
|
||||
// Version is locked to the env var value
|
||||
assert.Equal(t, "1.25", cli.ClientVersion())
|
||||
})
|
||||
|
||||
t.Run("newDockerClient allows version negotiation", func(t *testing.T) {
|
||||
// Test the actual production function.
|
||||
// Use DefaultDockerHost for cross-platform compatibility
|
||||
// (unix socket on Linux/macOS, named pipe on Windows).
|
||||
cli, err := newDockerClient(client.DefaultDockerHost)
|
||||
assert.NoError(t, err)
|
||||
defer cli.Close()
|
||||
|
||||
// Version is NOT locked to the env var value (1.25).
|
||||
// Instead, it uses the library's default version and will negotiate
|
||||
// with the server on first request. This is the key difference that
|
||||
// fixes the "version too old" error.
|
||||
assert.NotEqual(t, "1.25", cli.ClientVersion(),
|
||||
"client version should not be locked to DOCKER_API_VERSION env var")
|
||||
})
|
||||
}
|
||||
|
||||
// TestIsProjectScoped covers the predicate that drives whether the
|
||||
// project/services panels appear and whether the containers panel filters by
|
||||
// project. The "outside compose dir + -p" case is the regression we fixed
|
||||
// after PR #776 silently disabled it.
|
||||
func TestIsProjectScoped(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
inDockerComposeProject bool
|
||||
projectName string
|
||||
want bool
|
||||
}{
|
||||
{"inside compose dir, no -p", true, "", true},
|
||||
{"inside compose dir, with -p", true, "myproject", true},
|
||||
{"outside compose dir, no -p", false, "", false},
|
||||
{"outside compose dir, with -p", false, "myproject", true},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c := &DockerCommand{
|
||||
InDockerComposeProject: tc.inDockerComposeProject,
|
||||
Config: &config.AppConfig{ProjectName: tc.projectName},
|
||||
}
|
||||
assert.Equal(t, tc.want, c.IsProjectScoped())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io"
|
||||
|
||||
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||
"github.com/jesseduffield/lazydocker/pkg/i18n"
|
||||
|
|
@ -31,7 +31,7 @@ func NewDummyAppConfig() *config.AppConfig {
|
|||
// NewDummyLog creates a new dummy Log for testing
|
||||
func NewDummyLog() *logrus.Entry {
|
||||
log := logrus.New()
|
||||
log.Out = ioutil.Discard
|
||||
log.Out = io.Discard
|
||||
return log.WithField("test", "test")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,14 +4,12 @@ import (
|
|||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/samber/lo"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
|
@ -20,7 +18,7 @@ type Image struct {
|
|||
Name string
|
||||
Tag string
|
||||
ID string
|
||||
Image dockerTypes.ImageSummary
|
||||
Image image.Summary
|
||||
Client *client.Client
|
||||
OSCommand *OSCommand
|
||||
Log *logrus.Entry
|
||||
|
|
@ -28,7 +26,7 @@ type Image struct {
|
|||
}
|
||||
|
||||
// Remove removes the image
|
||||
func (i *Image) Remove(options dockerTypes.ImageRemoveOptions) error {
|
||||
func (i *Image) Remove(options image.RemoveOptions) error {
|
||||
if _, err := i.Client.ImageRemove(context.Background(), i.ID, options); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -94,16 +92,16 @@ func (i *Image) RenderHistory() (string, error) {
|
|||
|
||||
// RefreshImages returns a slice of docker images
|
||||
func (c *DockerCommand) RefreshImages() ([]*Image, error) {
|
||||
images, err := c.Client.ImageList(context.Background(), dockerTypes.ImageListOptions{})
|
||||
images, err := c.Client.ImageList(context.Background(), image.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ownImages := make([]*Image, len(images))
|
||||
|
||||
for i, image := range images {
|
||||
for i, img := range images {
|
||||
firstTag := ""
|
||||
tags := image.RepoTags
|
||||
tags := img.RepoTags
|
||||
if len(tags) > 0 {
|
||||
firstTag = tags[0]
|
||||
}
|
||||
|
|
@ -124,10 +122,10 @@ func (c *DockerCommand) RefreshImages() ([]*Image, error) {
|
|||
}
|
||||
|
||||
ownImages[i] = &Image{
|
||||
ID: image.ID,
|
||||
ID: img.ID,
|
||||
Name: name,
|
||||
Tag: tag,
|
||||
Image: image,
|
||||
Image: img,
|
||||
Client: c.Client,
|
||||
OSCommand: c.OSCommand,
|
||||
Log: c.Log,
|
||||
|
|
|
|||
54
pkg/commands/network.go
Normal file
54
pkg/commands/network.go
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Network : A docker Network
|
||||
type Network struct {
|
||||
Name string
|
||||
Network network.Inspect
|
||||
Client *client.Client
|
||||
OSCommand *OSCommand
|
||||
Log *logrus.Entry
|
||||
DockerCommand LimitedDockerCommand
|
||||
}
|
||||
|
||||
// RefreshNetworks gets the networks and stores them
|
||||
func (c *DockerCommand) RefreshNetworks() ([]*Network, error) {
|
||||
networks, err := c.Client.NetworkList(context.Background(), network.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ownNetworks := make([]*Network, len(networks))
|
||||
|
||||
for i, nw := range networks {
|
||||
ownNetworks[i] = &Network{
|
||||
Name: nw.Name,
|
||||
Network: nw,
|
||||
Client: c.Client,
|
||||
OSCommand: c.OSCommand,
|
||||
Log: c.Log,
|
||||
DockerCommand: c,
|
||||
}
|
||||
}
|
||||
|
||||
return ownNetworks, nil
|
||||
}
|
||||
|
||||
// PruneNetworks prunes networks
|
||||
func (c *DockerCommand) PruneNetworks() error {
|
||||
_, err := c.Client.NetworksPrune(context.Background(), filters.Args{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove removes the network
|
||||
func (v *Network) Remove() error {
|
||||
return v.Client.NetworkRemove(context.Background(), v.Name)
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ package commands
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
|
@ -22,13 +22,11 @@ import (
|
|||
|
||||
// Platform stores the os state
|
||||
type Platform struct {
|
||||
os string
|
||||
shell string
|
||||
shellArg string
|
||||
escapedQuote string
|
||||
openCommand string
|
||||
openLinkCommand string
|
||||
fallbackEscapedQuote string
|
||||
os string
|
||||
shell string
|
||||
shellArg string
|
||||
openCommand string
|
||||
openLinkCommand string
|
||||
}
|
||||
|
||||
// OSCommand holds all the os commands
|
||||
|
|
@ -89,7 +87,7 @@ func (c *OSCommand) RunExecutable(cmd *exec.Cmd) error {
|
|||
// ExecutableFromString takes a string like `docker ps -a` and returns an executable command for it
|
||||
func (c *OSCommand) ExecutableFromString(commandStr string) *exec.Cmd {
|
||||
splitCmd := str.ToArgv(commandStr)
|
||||
return c.command(splitCmd[0], splitCmd[1:]...)
|
||||
return c.NewCmd(splitCmd[0], splitCmd[1:]...)
|
||||
}
|
||||
|
||||
// Same as ExecutableFromString but cancellable via a context
|
||||
|
|
@ -98,6 +96,31 @@ func (c *OSCommand) ExecutableFromStringContext(ctx context.Context, commandStr
|
|||
return exec.CommandContext(ctx, splitCmd[0], splitCmd[1:]...)
|
||||
}
|
||||
|
||||
func (c *OSCommand) NewCmd(cmdName string, commandArgs ...string) *exec.Cmd {
|
||||
cmd := c.command(cmdName, commandArgs...)
|
||||
cmd.Env = os.Environ()
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *OSCommand) NewCommandStringWithShell(commandStr string) string {
|
||||
var quotedCommand string
|
||||
// Windows does not seem to like quotes around the command
|
||||
if c.Platform.os == "windows" {
|
||||
quotedCommand = strings.NewReplacer(
|
||||
"^", "^^",
|
||||
"&", "^&",
|
||||
"|", "^|",
|
||||
"<", "^<",
|
||||
">", "^>",
|
||||
"%", "^%",
|
||||
).Replace(commandStr)
|
||||
} else {
|
||||
quotedCommand = c.Quote(commandStr)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %s %s", c.Platform.shell, c.Platform.shellArg, quotedCommand)
|
||||
}
|
||||
|
||||
// RunCommand runs a command and just returns the error
|
||||
func (c *OSCommand) RunCommand(command string) error {
|
||||
_, err := c.RunCommandWithOutput(command)
|
||||
|
|
@ -116,16 +139,6 @@ func (c *OSCommand) FileType(path string) string {
|
|||
return "file"
|
||||
}
|
||||
|
||||
// RunDirectCommand wrapper around direct commands
|
||||
func (c *OSCommand) RunDirectCommand(command string) (string, error) {
|
||||
c.Log.WithField("command", command).Info("RunDirectCommand")
|
||||
|
||||
return sanitisedCommandOutput(
|
||||
c.command(c.Platform.shell, c.Platform.shellArg, command).
|
||||
CombinedOutput(),
|
||||
)
|
||||
}
|
||||
|
||||
func sanitisedCommandOutput(output []byte, err error) (string, error) {
|
||||
outputString := string(output)
|
||||
if err != nil {
|
||||
|
|
@ -180,22 +193,28 @@ func (c *OSCommand) EditFile(filename string) (*exec.Cmd, error) {
|
|||
return nil, errors.New("No editor defined in $VISUAL or $EDITOR")
|
||||
}
|
||||
|
||||
return c.PrepareSubProcess(editor, filename), nil
|
||||
}
|
||||
|
||||
// PrepareSubProcess iniPrepareSubProcessrocess then tells the Gui to switch to it
|
||||
func (c *OSCommand) PrepareSubProcess(cmdName string, commandArgs ...string) *exec.Cmd {
|
||||
return c.command(cmdName, commandArgs...)
|
||||
return c.NewCmd(editor, filename), nil
|
||||
}
|
||||
|
||||
// Quote wraps a message in platform-specific quotation marks
|
||||
func (c *OSCommand) Quote(message string) string {
|
||||
message = strings.Replace(message, "`", "\\`", -1)
|
||||
escapedQuote := c.Platform.escapedQuote
|
||||
if strings.Contains(message, c.Platform.escapedQuote) {
|
||||
escapedQuote = c.Platform.fallbackEscapedQuote
|
||||
var quote string
|
||||
if c.Platform.os == "windows" {
|
||||
quote = `\"`
|
||||
message = strings.NewReplacer(
|
||||
`"`, `"'"'"`,
|
||||
`\"`, `\\"`,
|
||||
).Replace(message)
|
||||
} else {
|
||||
quote = `"`
|
||||
message = strings.NewReplacer(
|
||||
`\`, `\\`,
|
||||
`"`, `\"`,
|
||||
`$`, `\$`,
|
||||
"`", "\\`",
|
||||
).Replace(message)
|
||||
}
|
||||
return escapedQuote + message + escapedQuote
|
||||
return quote + message + quote
|
||||
}
|
||||
|
||||
// Unquote removes wrapping quotations marks if they are present
|
||||
|
|
@ -221,7 +240,7 @@ func (c *OSCommand) AppendLineToFile(filename, line string) error {
|
|||
|
||||
// CreateTempFile writes a string to a new temp file and returns the file's name
|
||||
func (c *OSCommand) CreateTempFile(filename, content string) (string, error) {
|
||||
tmpfile, err := ioutil.TempFile("", filename)
|
||||
tmpfile, err := os.CreateTemp("", filename)
|
||||
if err != nil {
|
||||
c.Log.Error(err)
|
||||
return "", WrapError(err)
|
||||
|
|
@ -283,7 +302,7 @@ func (c *OSCommand) GetLazydockerPath() string {
|
|||
|
||||
// RunCustomCommand returns the pointer to a custom command
|
||||
func (c *OSCommand) RunCustomCommand(command string) *exec.Cmd {
|
||||
return c.PrepareSubProcess(c.Platform.shell, c.Platform.shellArg, command)
|
||||
return c.NewCmd(c.Platform.shell, c.Platform.shellArg, command)
|
||||
}
|
||||
|
||||
// PipeCommands runs a heap of commands and pipes their inputs/outputs together like A | B | C
|
||||
|
|
@ -323,7 +342,7 @@ func (c *OSCommand) PipeCommands(commandStrings ...string) error {
|
|||
c.Log.Error(err)
|
||||
}
|
||||
|
||||
if b, err := ioutil.ReadAll(stderr); err == nil {
|
||||
if b, err := io.ReadAll(stderr); err == nil {
|
||||
if len(b) > 0 {
|
||||
finalErrors = append(finalErrors, string(b))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,12 +9,10 @@ import (
|
|||
|
||||
func getPlatform() *Platform {
|
||||
return &Platform{
|
||||
os: runtime.GOOS,
|
||||
shell: "bash",
|
||||
shellArg: "-c",
|
||||
escapedQuote: "'",
|
||||
openCommand: "open {{filename}}",
|
||||
openLinkCommand: "open {{link}}",
|
||||
fallbackEscapedQuote: "\"",
|
||||
os: runtime.GOOS,
|
||||
shell: "bash",
|
||||
shellArg: "-c",
|
||||
openCommand: "open {{filename}}",
|
||||
openLinkCommand: "open {{link}}",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
|
@ -89,7 +89,7 @@ func TestOSCommandEditFile(t *testing.T) {
|
|||
|
||||
assert.EqualValues(t, "nano", name)
|
||||
|
||||
return nil
|
||||
return exec.Command("exit", "0")
|
||||
},
|
||||
func(env string) string {
|
||||
if env == "VISUAL" {
|
||||
|
|
@ -111,7 +111,7 @@ func TestOSCommandEditFile(t *testing.T) {
|
|||
|
||||
assert.EqualValues(t, "emacs", name)
|
||||
|
||||
return nil
|
||||
return exec.Command("exit", "0")
|
||||
},
|
||||
func(env string) string {
|
||||
if env == "EDITOR" {
|
||||
|
|
@ -133,7 +133,7 @@ func TestOSCommandEditFile(t *testing.T) {
|
|||
|
||||
assert.EqualValues(t, "vi", name)
|
||||
|
||||
return nil
|
||||
return exec.Command("exit", "0")
|
||||
},
|
||||
func(env string) string {
|
||||
return ""
|
||||
|
|
@ -153,13 +153,14 @@ func TestOSCommandEditFile(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestOSCommandQuote is a function.
|
||||
func TestOSCommandQuote(t *testing.T) {
|
||||
osCommand := NewDummyOSCommand()
|
||||
|
||||
osCommand.Platform.os = "linux"
|
||||
|
||||
actual := osCommand.Quote("hello `test`")
|
||||
|
||||
expected := osCommand.Platform.escapedQuote + "hello \\`test\\`" + osCommand.Platform.escapedQuote
|
||||
expected := "\"hello \\`test\\`\""
|
||||
|
||||
assert.EqualValues(t, expected, actual)
|
||||
}
|
||||
|
|
@ -169,12 +170,10 @@ func TestOSCommandQuoteSingleQuote(t *testing.T) {
|
|||
osCommand := NewDummyOSCommand()
|
||||
|
||||
osCommand.Platform.os = "linux"
|
||||
osCommand.Platform.fallbackEscapedQuote = "\""
|
||||
osCommand.Platform.escapedQuote = "'"
|
||||
|
||||
actual := osCommand.Quote("hello 'test'")
|
||||
|
||||
expected := osCommand.Platform.fallbackEscapedQuote + "hello 'test'" + osCommand.Platform.fallbackEscapedQuote
|
||||
expected := `"hello 'test'"`
|
||||
|
||||
assert.EqualValues(t, expected, actual)
|
||||
}
|
||||
|
|
@ -187,7 +186,20 @@ func TestOSCommandQuoteDoubleQuote(t *testing.T) {
|
|||
|
||||
actual := osCommand.Quote(`hello "test"`)
|
||||
|
||||
expected := osCommand.Platform.escapedQuote + "hello \"test\"" + osCommand.Platform.escapedQuote
|
||||
expected := `"hello \"test\""`
|
||||
|
||||
assert.EqualValues(t, expected, actual)
|
||||
}
|
||||
|
||||
// TestOSCommandQuoteWindows tests the quote function for Windows
|
||||
func TestOSCommandQuoteWindows(t *testing.T) {
|
||||
osCommand := NewDummyOSCommand()
|
||||
|
||||
osCommand.Platform.os = "windows"
|
||||
|
||||
actual := osCommand.Quote(`hello "test" 'test2'`)
|
||||
|
||||
expected := `\"hello "'"'"test"'"'" 'test2'\"`
|
||||
|
||||
assert.EqualValues(t, expected, actual)
|
||||
}
|
||||
|
|
@ -277,7 +289,7 @@ func TestOSCommandCreateTempFile(t *testing.T) {
|
|||
func(path string, err error) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
content, err := ioutil.ReadFile(path)
|
||||
content, err := os.ReadFile(path)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "content", string(content))
|
||||
|
|
@ -291,3 +303,53 @@ func TestOSCommandCreateTempFile(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOSCommandExecutableFromStringWithShellLinux(t *testing.T) {
|
||||
osCommand := NewDummyOSCommand()
|
||||
|
||||
osCommand.Platform.os = "linux"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
commandStr string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
"success",
|
||||
"pwd",
|
||||
fmt.Sprintf("%v %v %v", osCommand.Platform.shell, osCommand.Platform.shellArg, "\"pwd\""),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := osCommand.NewCommandStringWithShell(tt.commandStr)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOSCommandNewCommandStringWithShellWindows(t *testing.T) {
|
||||
osCommand := NewDummyOSCommand()
|
||||
|
||||
osCommand.Platform.os = "windows"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
commandStr string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
"success",
|
||||
"pwd",
|
||||
fmt.Sprintf("%v %v %v", osCommand.Platform.shell, osCommand.Platform.shellArg, "pwd"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := osCommand.NewCommandStringWithShell(tt.commandStr)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@ package commands
|
|||
|
||||
func getPlatform() *Platform {
|
||||
return &Platform{
|
||||
os: "windows",
|
||||
shell: "cmd",
|
||||
shellArg: "/c",
|
||||
escapedQuote: `\"`,
|
||||
fallbackEscapedQuote: "\\'",
|
||||
os: "windows",
|
||||
shell: "cmd",
|
||||
shellArg: "/c",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"context"
|
||||
"os/exec"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
|
@ -13,6 +13,7 @@ import (
|
|||
type Service struct {
|
||||
Name string
|
||||
ID string
|
||||
ProjectName string
|
||||
OSCommand *OSCommand
|
||||
Log *logrus.Entry
|
||||
Container *Container
|
||||
|
|
@ -20,7 +21,7 @@ type Service struct {
|
|||
}
|
||||
|
||||
// Remove removes the service's containers
|
||||
func (s *Service) Remove(options dockerTypes.ContainerRemoveOptions) error {
|
||||
func (s *Service) Remove(options container.RemoveOptions) error {
|
||||
return s.Container.Remove(options)
|
||||
}
|
||||
|
||||
|
|
@ -39,7 +40,7 @@ func (s *Service) Restart() error {
|
|||
return s.runCommand(s.OSCommand.Config.UserConfig.CommandTemplates.RestartService)
|
||||
}
|
||||
|
||||
// Restart starts the service
|
||||
// Start starts the service
|
||||
func (s *Service) Start() error {
|
||||
return s.runCommand(s.OSCommand.Config.UserConfig.CommandTemplates.StartService)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
|
|
@ -37,7 +36,7 @@ func NewSSHHandler(oSCommand CmdKiller) *SSHHandler {
|
|||
return (&net.Dialer{}).DialContext(ctx, network, addr)
|
||||
},
|
||||
startCmd: func(cmd *exec.Cmd) error { return cmd.Start() },
|
||||
tempDir: ioutil.TempDir,
|
||||
tempDir: os.MkdirTemp,
|
||||
getenv: os.Getenv,
|
||||
setenv: os.Setenv,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package commands
|
|||
import (
|
||||
"context"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
|
@ -12,7 +12,7 @@ import (
|
|||
// Volume : A docker Volume
|
||||
type Volume struct {
|
||||
Name string
|
||||
Volume *dockerTypes.Volume
|
||||
Volume *volume.Volume
|
||||
Client *client.Client
|
||||
OSCommand *OSCommand
|
||||
Log *logrus.Entry
|
||||
|
|
@ -21,7 +21,7 @@ type Volume struct {
|
|||
|
||||
// RefreshVolumes gets the volumes and stores them
|
||||
func (c *DockerCommand) RefreshVolumes() ([]*Volume, error) {
|
||||
result, err := c.Client.VolumeList(context.Background(), filters.Args{})
|
||||
result, err := c.Client.VolumeList(context.Background(), volume.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -30,10 +30,10 @@ func (c *DockerCommand) RefreshVolumes() ([]*Volume, error) {
|
|||
|
||||
ownVolumes := make([]*Volume, len(volumes))
|
||||
|
||||
for i, volume := range volumes {
|
||||
for i, vol := range volumes {
|
||||
ownVolumes[i] = &Volume{
|
||||
Name: volume.Name,
|
||||
Volume: volume,
|
||||
Name: vol.Name,
|
||||
Volume: vol,
|
||||
Client: c.Client,
|
||||
OSCommand: c.OSCommand,
|
||||
Log: c.Log,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
|
@ -71,6 +70,7 @@ type UserConfig struct {
|
|||
type ThemeConfig struct {
|
||||
ActiveBorderColor []string `yaml:"activeBorderColor,omitempty"`
|
||||
InactiveBorderColor []string `yaml:"inactiveBorderColor,omitempty"`
|
||||
SelectedLineBgColor []string `yaml:"selectedLineBgColor,omitempty"`
|
||||
OptionsTextColor []string `yaml:"optionsTextColor,omitempty"`
|
||||
}
|
||||
|
||||
|
|
@ -130,6 +130,18 @@ type GuiConfig struct {
|
|||
// When true, increases vertical space used by focused side panel,
|
||||
// creating an accordion effect
|
||||
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
|
||||
|
||||
// ScreenMode allow user to specify which screen mode will be used on startup
|
||||
ScreenMode string `yaml:"screenMode,omitempty"`
|
||||
|
||||
// Determines the style of the container status and container health display in the
|
||||
// containers panel. "long": full words (default), "short": one or two characters,
|
||||
// "icon": unicode emoji.
|
||||
ContainerStatusHealthStyle string `yaml:"containerStatusHealthStyle"`
|
||||
|
||||
// Window border style.
|
||||
// One of 'rounded' (default) | 'single' | 'double' | 'hidden'
|
||||
Border string `yaml:"border"`
|
||||
}
|
||||
|
||||
// CommandTemplatesConfig determines what commands actually get called when we
|
||||
|
|
@ -156,7 +168,7 @@ type CommandTemplatesConfig struct {
|
|||
|
||||
// DockerCompose is for your docker-compose command. You may want to combine a
|
||||
// few different docker-compose.yml files together, in which case you can set
|
||||
// this to "docker-compose -f foo/docker-compose.yml -f
|
||||
// this to "docker compose -f foo/docker-compose.yml -f
|
||||
// bah/docker-compose.yml". The reason that the other docker-compose command
|
||||
// templates all start with {{ .DockerCompose }} is so that they can make use
|
||||
// of whatever you've set in this value rather than you having to copy and
|
||||
|
|
@ -188,7 +200,7 @@ type CommandTemplatesConfig struct {
|
|||
// and ensure they're running before trying to run the service at hand
|
||||
RecreateService string `yaml:"recreateService,omitempty"`
|
||||
|
||||
// AllLogs is for showing what you get from doing `docker-compose logs`. It
|
||||
// AllLogs is for showing what you get from doing `docker compose logs`. It
|
||||
// combines all the logs together
|
||||
AllLogs string `yaml:"allLogs,omitempty"`
|
||||
|
||||
|
|
@ -286,6 +298,9 @@ type CustomCommands struct {
|
|||
|
||||
// Volumes contains the custom commands for volumes
|
||||
Volumes []CustomCommand `yaml:"volumes,omitempty"`
|
||||
|
||||
// Networks contains the custom commands for networks
|
||||
Networks []CustomCommand `yaml:"networks,omitempty"`
|
||||
}
|
||||
|
||||
// Replacements contains the stuff relating to rendering a container's info
|
||||
|
|
@ -306,6 +321,10 @@ type CustomCommand struct {
|
|||
// option where the output plays in the main panel.
|
||||
Attach bool `yaml:"attach"`
|
||||
|
||||
// Shell indicates whether to invoke the Command on a shell or not.
|
||||
// Example of a bash invoked command: `/bin/bash -c "{Command}".
|
||||
Shell bool `yaml:"shell"`
|
||||
|
||||
// Command is the command we want to run. We can use the go templates here as
|
||||
// well. One example might be `{{ .DockerCompose }} exec {{ .Service.Name }}
|
||||
// /bin/sh`
|
||||
|
|
@ -324,6 +343,7 @@ type CustomCommand struct {
|
|||
type LogsConfig struct {
|
||||
Timestamps bool `yaml:"timestamps,omitempty"`
|
||||
Since string `yaml:"since,omitempty"`
|
||||
Tail string `yaml:"tail,omitempty"`
|
||||
}
|
||||
|
||||
// GetDefaultConfig returns the application default configuration NOTE (to
|
||||
|
|
@ -345,23 +365,27 @@ func GetDefaultConfig() UserConfig {
|
|||
Theme: ThemeConfig{
|
||||
ActiveBorderColor: []string{"green", "bold"},
|
||||
InactiveBorderColor: []string{"default"},
|
||||
SelectedLineBgColor: []string{"blue"},
|
||||
OptionsTextColor: []string{"blue"},
|
||||
},
|
||||
ShowAllContainers: false,
|
||||
ReturnImmediately: false,
|
||||
WrapMainPanel: true,
|
||||
LegacySortContainers: false,
|
||||
SidePanelWidth: 0.3333,
|
||||
ShowBottomLine: true,
|
||||
ExpandFocusedSidePanel: false,
|
||||
ShowAllContainers: false,
|
||||
ReturnImmediately: false,
|
||||
WrapMainPanel: true,
|
||||
LegacySortContainers: false,
|
||||
SidePanelWidth: 0.3333,
|
||||
ShowBottomLine: true,
|
||||
ExpandFocusedSidePanel: false,
|
||||
ScreenMode: "normal",
|
||||
ContainerStatusHealthStyle: "long",
|
||||
},
|
||||
ConfirmOnQuit: false,
|
||||
Logs: LogsConfig{
|
||||
Timestamps: false,
|
||||
Since: "60m",
|
||||
Tail: "",
|
||||
},
|
||||
CommandTemplates: CommandTemplatesConfig{
|
||||
DockerCompose: "docker-compose",
|
||||
DockerCompose: "docker compose",
|
||||
RestartService: "{{ .DockerCompose }} restart {{ .Service.Name }}",
|
||||
StartService: "{{ .DockerCompose }} start {{ .Service.Name }}",
|
||||
Up: "{{ .DockerCompose }} up -d",
|
||||
|
|
@ -464,10 +488,11 @@ type AppConfig struct {
|
|||
UserConfig *UserConfig
|
||||
ConfigDir string
|
||||
ProjectDir string
|
||||
ProjectName string
|
||||
}
|
||||
|
||||
// NewAppConfig makes a new app config
|
||||
func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag bool, composeFiles []string, projectDir string) (*AppConfig, error) {
|
||||
func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag bool, composeFiles []string, projectDir string, projectName string) (*AppConfig, error) {
|
||||
configDir, err := findOrCreateConfigDir(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -478,7 +503,7 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Pass compose files as individual -f flags to docker-compose
|
||||
// Pass compose files as individual -f flags to docker compose
|
||||
if len(composeFiles) > 0 {
|
||||
userConfig.CommandTemplates.DockerCompose += " -f " + strings.Join(composeFiles, " -f ")
|
||||
}
|
||||
|
|
@ -493,6 +518,7 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg
|
|||
UserConfig: userConfig,
|
||||
ConfigDir: configDir,
|
||||
ProjectDir: projectDir,
|
||||
ProjectName: projectName,
|
||||
}
|
||||
|
||||
return appConfig, nil
|
||||
|
|
@ -548,7 +574,7 @@ func loadUserConfig(configDir string, base *UserConfig) (*UserConfig, error) {
|
|||
}
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile(fileName)
|
||||
content, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@ import (
|
|||
|
||||
func TestDockerComposeCommandNoFiles(t *testing.T) {
|
||||
composeFiles := []string{}
|
||||
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles, "projectDir")
|
||||
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles, "projectDir", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
|
||||
actual := conf.UserConfig.CommandTemplates.DockerCompose
|
||||
expected := "docker-compose"
|
||||
expected := "docker compose"
|
||||
if actual != expected {
|
||||
t.Fatalf("Expected %s but got %s", expected, actual)
|
||||
}
|
||||
|
|
@ -23,13 +23,13 @@ func TestDockerComposeCommandNoFiles(t *testing.T) {
|
|||
|
||||
func TestDockerComposeCommandSingleFile(t *testing.T) {
|
||||
composeFiles := []string{"one.yml"}
|
||||
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles, "projectDir")
|
||||
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles, "projectDir", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
|
||||
actual := conf.UserConfig.CommandTemplates.DockerCompose
|
||||
expected := "docker-compose -f one.yml"
|
||||
expected := "docker compose -f one.yml"
|
||||
if actual != expected {
|
||||
t.Fatalf("Expected %s but got %s", expected, actual)
|
||||
}
|
||||
|
|
@ -37,13 +37,13 @@ func TestDockerComposeCommandSingleFile(t *testing.T) {
|
|||
|
||||
func TestDockerComposeCommandMultipleFiles(t *testing.T) {
|
||||
composeFiles := []string{"one.yml", "two.yml", "three.yml"}
|
||||
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles, "projectDir")
|
||||
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles, "projectDir", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
|
||||
actual := conf.UserConfig.CommandTemplates.DockerCompose
|
||||
expected := "docker-compose -f one.yml -f two.yml -f three.yml"
|
||||
expected := "docker compose -f one.yml -f two.yml -f three.yml"
|
||||
if actual != expected {
|
||||
t.Fatalf("Expected %s but got %s", expected, actual)
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ func TestDockerComposeCommandMultipleFiles(t *testing.T) {
|
|||
func TestWritingToConfigFile(t *testing.T) {
|
||||
// init the AppConfig
|
||||
emptyComposeFiles := []string{}
|
||||
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, emptyComposeFiles, "projectDir")
|
||||
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, emptyComposeFiles, "projectDir", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,14 +175,31 @@ func (gui *Gui) sidePanelChildren(width int, height int) []*boxlayout.Box {
|
|||
return defaultBox
|
||||
}
|
||||
|
||||
return append([]*boxlayout.Box{
|
||||
{
|
||||
// The project panel is compact (Size: 3) when not focused, but expands
|
||||
// when focused to show the list of projects. This only applies when the
|
||||
// project panel is actually visible (i.e. we are inside a compose project).
|
||||
if len(sideWindowNames) > 0 && sideWindowNames[0] == "project" {
|
||||
projectBox := &boxlayout.Box{
|
||||
Window: sideWindowNames[0],
|
||||
Size: 3,
|
||||
},
|
||||
}, lo.Map(sideWindowNames[1:], func(window string, _ int) *boxlayout.Box {
|
||||
}
|
||||
if currentWindow == sideWindowNames[0] {
|
||||
projectBox = &boxlayout.Box{
|
||||
Window: sideWindowNames[0],
|
||||
Weight: 2,
|
||||
}
|
||||
}
|
||||
|
||||
return append([]*boxlayout.Box{
|
||||
projectBox,
|
||||
}, lo.Map(sideWindowNames[1:], func(window string, _ int) *boxlayout.Box {
|
||||
return accordionBox(&boxlayout.Box{Window: window, Weight: 1})
|
||||
})...)
|
||||
}
|
||||
|
||||
return lo.Map(sideWindowNames, func(window string, _ int) *boxlayout.Box {
|
||||
return accordionBox(&boxlayout.Box{Window: window, Weight: 1})
|
||||
})...)
|
||||
})
|
||||
} else {
|
||||
squashedHeight := 1
|
||||
if height >= 21 {
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ func (gui *Gui) getConfirmationPanelDimensions(wrap bool, prompt string) (int, i
|
|||
|
||||
func (gui *Gui) createPromptPanel(title string, handleConfirm func(*gocui.Gui, *gocui.View) error) error {
|
||||
gui.onNewPopupPanel()
|
||||
err := gui.prepareConfirmationPanel(title, "", false)
|
||||
err := gui.prepareConfirmationPanel(title, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -72,17 +72,13 @@ func (gui *Gui) createPromptPanel(title string, handleConfirm func(*gocui.Gui, *
|
|||
return gui.setKeyBindings(gui.g, handleConfirm, nil)
|
||||
}
|
||||
|
||||
func (gui *Gui) prepareConfirmationPanel(title, prompt string, hasLoader bool) error {
|
||||
func (gui *Gui) prepareConfirmationPanel(title, prompt string) error {
|
||||
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(true, prompt)
|
||||
confirmationView := gui.Views.Confirmation
|
||||
_, err := gui.g.SetView("confirmation", x0, y0, x1, y1, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
confirmationView.HasLoader = hasLoader
|
||||
if hasLoader {
|
||||
gui.g.StartTicking()
|
||||
}
|
||||
confirmationView.Title = title
|
||||
confirmationView.Visible = true
|
||||
gui.g.Update(func(g *gocui.Gui) error {
|
||||
|
|
@ -100,10 +96,10 @@ func (gui *Gui) onNewPopupPanel() {
|
|||
// The golangcilint unparam linter complains that handleClose is alwans nil but one day it won't be nil.
|
||||
// nolint:unparam
|
||||
func (gui *Gui) createConfirmationPanel(title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
|
||||
return gui.createPopupPanel(title, prompt, false, handleConfirm, handleClose)
|
||||
return gui.createPopupPanel(title, prompt, handleConfirm, handleClose)
|
||||
}
|
||||
|
||||
func (gui *Gui) createPopupPanel(title, prompt string, hasLoader bool, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
|
||||
func (gui *Gui) createPopupPanel(title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
|
||||
gui.onNewPopupPanel()
|
||||
gui.g.Update(func(g *gocui.Gui) error {
|
||||
if gui.currentViewName() == "confirmation" {
|
||||
|
|
@ -111,7 +107,7 @@ func (gui *Gui) createPopupPanel(title, prompt string, hasLoader bool, handleCon
|
|||
gui.Log.Error(err.Error())
|
||||
}
|
||||
}
|
||||
err := gui.prepareConfirmationPanel(title, prompt, hasLoader)
|
||||
err := gui.prepareConfirmationPanel(title, prompt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
"os/signal"
|
||||
"time"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
|
|
@ -104,19 +104,39 @@ func (gui *Gui) promptToReturn() {
|
|||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) writeContainerLogs(container *commands.Container, ctx context.Context, writer io.Writer) error {
|
||||
readCloser, err := gui.DockerCommand.Client.ContainerLogs(ctx, container.ID, dockerTypes.ContainerLogsOptions{
|
||||
func (gui *Gui) writeContainerLogs(ctr *commands.Container, ctx context.Context, writer io.Writer) error {
|
||||
readCloser, err := gui.DockerCommand.Client.ContainerLogs(ctx, ctr.ID, container.LogsOptions{
|
||||
ShowStdout: true,
|
||||
ShowStderr: true,
|
||||
Timestamps: gui.Config.UserConfig.Logs.Timestamps,
|
||||
Since: gui.Config.UserConfig.Logs.Since,
|
||||
Tail: gui.Config.UserConfig.Logs.Tail,
|
||||
Follow: true,
|
||||
})
|
||||
if err != nil {
|
||||
gui.Log.Error(err)
|
||||
return err
|
||||
}
|
||||
defer readCloser.Close()
|
||||
|
||||
if container.DetailsLoaded() && container.Details.Config.Tty {
|
||||
if !ctr.DetailsLoaded() {
|
||||
// loop until the details load or context is cancelled, using timer
|
||||
ticker := time.NewTicker(time.Millisecond * 100)
|
||||
defer ticker.Stop()
|
||||
outer:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-ticker.C:
|
||||
if ctr.DetailsLoaded() {
|
||||
break outer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ctr.Details.Config.Tty {
|
||||
_, err = io.Copy(writer, readCloser)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -2,12 +2,11 @@ package gui
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
|
|
@ -28,7 +27,7 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
|
|||
}
|
||||
|
||||
return !lo.SomeBy(gui.Panels.Services.List.GetAllItems(), func(service *commands.Service) bool {
|
||||
return service.Name == container.ServiceName
|
||||
return service.Name == container.ServiceName && service.ProjectName == container.ProjectName
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -84,20 +83,42 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
|
|||
return sortContainers(a, b, gui.Config.UserConfig.Gui.LegacySortContainers)
|
||||
},
|
||||
Filter: func(container *commands.Container) bool {
|
||||
// Note that this is O(N*M) time complexity where N is the number of services
|
||||
// and M is the number of containers. We expect N to be small but M may be large,
|
||||
// so we will need to keep an eye on this.
|
||||
if !gui.Config.UserConfig.Gui.ShowAllContainers && !isStandaloneContainer(container) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !gui.State.ShowExitedContainers && container.Container.State == "exited" {
|
||||
return false
|
||||
}
|
||||
|
||||
// When project-scoped, apply project and standalone filtering.
|
||||
// Otherwise all containers are shown in a flat list regardless
|
||||
// of which compose project they belong to.
|
||||
if gui.DockerCommand.IsProjectScoped() {
|
||||
// This check must be inside the IsProjectScoped guard: when
|
||||
// not project-scoped, services are still derived from container
|
||||
// labels, so compose-managed containers from other projects
|
||||
// would be incorrectly hidden.
|
||||
//
|
||||
// Note that this is O(N*M) time complexity where N is the number of services
|
||||
// and M is the number of containers. We expect N to be small but M may be large,
|
||||
// so we will need to keep an eye on this.
|
||||
if !gui.Config.UserConfig.Gui.ShowAllContainers && !isStandaloneContainer(container) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Filter by selected project. Containers with no project (truly
|
||||
// standalone, not from any compose project) are always shown.
|
||||
selectedProject := gui.getSelectedProjectName()
|
||||
if selectedProject == "" {
|
||||
selectedProject = gui.DockerCommand.LocalProjectName
|
||||
}
|
||||
if selectedProject != "" && container.ProjectName != "" && container.ProjectName != selectedProject {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
GetTableCells: presentation.GetContainerDisplayStrings,
|
||||
GetTableCells: func(container *commands.Container) []string {
|
||||
return presentation.GetContainerDisplayStrings(&gui.Config.UserConfig.Gui, container)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -200,12 +221,12 @@ func (gui *Gui) containerConfigStr(container *commands.Container) string {
|
|||
output += "none\n"
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(&container.Details, "", " ")
|
||||
data, err := utils.MarshalIntoYaml(&container.Details)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Error marshalling container details: %v", err)
|
||||
}
|
||||
|
||||
output += fmt.Sprintf("\nFull details:\n\n%s", string(data))
|
||||
output += fmt.Sprintf("\nFull details:\n\n%s", utils.ColoredYamlString(string(data)))
|
||||
|
||||
return output
|
||||
}
|
||||
|
|
@ -255,7 +276,6 @@ func (gui *Gui) refreshContainersAndServices() error {
|
|||
selectedService, isServiceSelected := gui.Panels.Services.List.TryGet(originalSelectedLineIdx)
|
||||
|
||||
containers, services, err := gui.DockerCommand.RefreshContainersAndServices(
|
||||
gui.Panels.Services.List.GetAllItems(),
|
||||
gui.Panels.Containers.List.GetAllItems(),
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -282,10 +302,8 @@ func (gui *Gui) refreshContainersAndServices() error {
|
|||
}
|
||||
|
||||
func (gui *Gui) renderContainersAndServices() error {
|
||||
if gui.DockerCommand.InDockerComposeProject {
|
||||
if err := gui.Panels.Services.RerenderList(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.Panels.Services.RerenderList(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gui.Panels.Containers.RerenderList(); err != nil {
|
||||
|
|
@ -302,19 +320,19 @@ func (gui *Gui) handleHideStoppedContainers(g *gocui.Gui, v *gocui.View) error {
|
|||
}
|
||||
|
||||
func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
handleMenuPress := func(configOptions dockerTypes.ContainerRemoveOptions) error {
|
||||
handleMenuPress := func(configOptions container.RemoveOptions) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
||||
if err := container.Remove(configOptions); err != nil {
|
||||
if err := ctr.Remove(configOptions); err != nil {
|
||||
if commands.HasErrorCode(err, commands.MustStopContainer) {
|
||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.MustForceToRemoveContainer, func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
||||
configOptions.Force = true
|
||||
return container.Remove(configOptions)
|
||||
return ctr.Remove(configOptions)
|
||||
})
|
||||
}, nil)
|
||||
}
|
||||
|
|
@ -326,12 +344,12 @@ func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
|||
|
||||
menuItems := []*types.MenuItem{
|
||||
{
|
||||
LabelColumns: []string{gui.Tr.Remove, "docker rm " + container.ID[1:10]},
|
||||
OnPress: func() error { return handleMenuPress(dockerTypes.ContainerRemoveOptions{}) },
|
||||
LabelColumns: []string{gui.Tr.Remove, "docker rm " + ctr.ID[1:10]},
|
||||
OnPress: func() error { return handleMenuPress(container.RemoveOptions{}) },
|
||||
},
|
||||
{
|
||||
LabelColumns: []string{gui.Tr.RemoveWithVolumes, "docker rm --volumes " + container.ID[1:10]},
|
||||
OnPress: func() error { return handleMenuPress(dockerTypes.ContainerRemoveOptions{RemoveVolumes: true}) },
|
||||
LabelColumns: []string{gui.Tr.RemoveWithVolumes, "docker rm --volumes " + ctr.ID[1:10]},
|
||||
OnPress: func() error { return handleMenuPress(container.RemoveOptions{RemoveVolumes: true}) },
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -358,23 +376,23 @@ func (gui *Gui) PauseContainer(container *commands.Container) error {
|
|||
}
|
||||
|
||||
func (gui *Gui) handleContainerPause(g *gocui.Gui, v *gocui.View) error {
|
||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.PauseContainer(container)
|
||||
return gui.PauseContainer(ctr)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error {
|
||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.StopContainer, func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
|
||||
if err := container.Stop(); err != nil {
|
||||
if err := ctr.Stop(); err != nil {
|
||||
return gui.createErrorPanel(err.Error())
|
||||
}
|
||||
|
||||
|
|
@ -384,13 +402,13 @@ func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error {
|
|||
}
|
||||
|
||||
func (gui *Gui) handleContainerRestart(g *gocui.Gui, v *gocui.View) error {
|
||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
|
||||
if err := container.Restart(); err != nil {
|
||||
if err := ctr.Restart(); err != nil {
|
||||
return gui.createErrorPanel(err.Error())
|
||||
}
|
||||
|
||||
|
|
@ -399,17 +417,17 @@ func (gui *Gui) handleContainerRestart(g *gocui.Gui, v *gocui.View) error {
|
|||
}
|
||||
|
||||
func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error {
|
||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
c, err := container.Attach()
|
||||
c, err := ctr.Attach()
|
||||
if err != nil {
|
||||
return gui.createErrorPanel(err.Error())
|
||||
}
|
||||
|
||||
return gui.runSubprocess(c)
|
||||
return gui.runSubprocessWithMessage(c, gui.Tr.DetachFromContainerShortCut)
|
||||
}
|
||||
|
||||
func (gui *Gui) handlePruneContainers() error {
|
||||
|
|
@ -425,23 +443,23 @@ func (gui *Gui) handlePruneContainers() error {
|
|||
}
|
||||
|
||||
func (gui *Gui) handleContainerViewLogs(g *gocui.Gui, v *gocui.View) error {
|
||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
gui.renderLogsToStdout(container)
|
||||
gui.renderLogsToStdout(ctr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleContainersExecShell(g *gocui.Gui, v *gocui.View) error {
|
||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.containerExecShell(container)
|
||||
return gui.containerExecShell(ctr)
|
||||
}
|
||||
|
||||
func (gui *Gui) containerExecShell(container *commands.Container) error {
|
||||
|
|
@ -457,13 +475,13 @@ func (gui *Gui) containerExecShell(container *commands.Container) error {
|
|||
}
|
||||
|
||||
func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error {
|
||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
|
||||
Container: container,
|
||||
Container: ctr,
|
||||
})
|
||||
|
||||
customCommands := gui.Config.UserConfig.CustomCommands.Containers
|
||||
|
|
@ -474,8 +492,8 @@ func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error
|
|||
func (gui *Gui) handleStopContainers() error {
|
||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmStopContainers, func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
|
||||
for _, container := range gui.Panels.Containers.List.GetAllItems() {
|
||||
if err := container.Stop(); err != nil {
|
||||
for _, ctr := range gui.Panels.Containers.List.GetAllItems() {
|
||||
if err := ctr.Stop(); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -488,8 +506,8 @@ func (gui *Gui) handleStopContainers() error {
|
|||
func (gui *Gui) handleRemoveContainers() error {
|
||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmRemoveContainers, func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
||||
for _, container := range gui.Panels.Containers.List.GetAllItems() {
|
||||
if err := container.Remove(dockerTypes.ContainerRemoveOptions{Force: true}); err != nil {
|
||||
for _, ctr := range gui.Panels.Containers.List.GetAllItems() {
|
||||
if err := ctr.Remove(container.RemoveOptions{Force: true}); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -523,21 +541,21 @@ func (gui *Gui) handleContainersBulkCommand(g *gocui.Gui, v *gocui.View) error {
|
|||
|
||||
// Open first port in browser
|
||||
func (gui *Gui) handleContainersOpenInBrowserCommand(g *gocui.Gui, v *gocui.View) error {
|
||||
container, err := gui.Panels.Containers.GetSelectedItem()
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.openContainerInBrowser(container)
|
||||
return gui.openContainerInBrowser(ctr)
|
||||
}
|
||||
|
||||
func (gui *Gui) openContainerInBrowser(container *commands.Container) error {
|
||||
func (gui *Gui) openContainerInBrowser(ctr *commands.Container) error {
|
||||
// skip if no any ports
|
||||
if len(container.Container.Ports) == 0 {
|
||||
if len(ctr.Container.Ports) == 0 {
|
||||
return nil
|
||||
}
|
||||
// skip if the first port is not published
|
||||
port := container.Container.Ports[0]
|
||||
port := ctr.Container.Ports[0]
|
||||
if port.IP == "" {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,13 @@ func (gui *Gui) createCommandMenu(customCommands []config.CustomCommand, command
|
|||
return command.InternalFunction()
|
||||
}
|
||||
|
||||
if command.Shell {
|
||||
resolvedCommand = gui.OSCommand.NewCommandStringWithShell(resolvedCommand)
|
||||
}
|
||||
|
||||
// if we have a command for attaching, we attach and return the subprocess error
|
||||
if command.Attach {
|
||||
cmd := gui.OSCommand.ExecutableFromString(resolvedCommand)
|
||||
return gui.runSubprocess(cmd)
|
||||
return gui.runSubprocess(gui.OSCommand.ExecutableFromString(resolvedCommand))
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(waitingStatus, func() error {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
|
||||
|
|
@ -23,9 +23,6 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// OverlappingEdges determines if panel edges overlap
|
||||
var OverlappingEdges = false
|
||||
|
||||
// Gui wraps the gocui Gui object which handles rendering and events
|
||||
type Gui struct {
|
||||
g *gocui.Gui
|
||||
|
|
@ -56,6 +53,7 @@ type Panels struct {
|
|||
Containers *panels.SideListPanel[*commands.Container]
|
||||
Images *panels.SideListPanel[*commands.Image]
|
||||
Volumes *panels.SideListPanel[*commands.Volume]
|
||||
Networks *panels.SideListPanel[*commands.Network]
|
||||
Menu *panels.SideListPanel[*types.MenuItem]
|
||||
}
|
||||
|
||||
|
|
@ -113,6 +111,19 @@ const (
|
|||
SCREEN_FULL
|
||||
)
|
||||
|
||||
func getScreenMode(config *config.AppConfig) WindowMaximisation {
|
||||
switch config.UserConfig.Gui.ScreenMode {
|
||||
case "normal":
|
||||
return SCREEN_NORMAL
|
||||
case "half":
|
||||
return SCREEN_HALF
|
||||
case "fullscreen":
|
||||
return SCREEN_FULL
|
||||
default:
|
||||
return SCREEN_NORMAL
|
||||
}
|
||||
}
|
||||
|
||||
// NewGui builds a new gui handler
|
||||
func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand *commands.OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*Gui, error) {
|
||||
initialState := guiState{
|
||||
|
|
@ -125,6 +136,7 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
|
|||
ViewStack: []string{},
|
||||
|
||||
ShowExitedContainers: true,
|
||||
ScreenMode: getScreenMode(config),
|
||||
}
|
||||
|
||||
gui := &Gui{
|
||||
|
|
@ -161,11 +173,9 @@ func (gui *Gui) goEvery(interval time.Duration, function func() error) {
|
|||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
if gui.PauseBackgroundThreads {
|
||||
return
|
||||
if !gui.PauseBackgroundThreads {
|
||||
_ = function()
|
||||
}
|
||||
|
||||
_ = function()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
|
@ -175,7 +185,10 @@ func (gui *Gui) Run() error {
|
|||
// closing our task manager which in turn closes the current task if there is any, so we aren't leaving processes lying around after closing lazydocker
|
||||
defer gui.taskManager.Close()
|
||||
|
||||
g, err := gocui.NewGui(gocui.OutputTrue, OverlappingEdges, gocui.NORMAL, false, map[rune]string{})
|
||||
g, err := gocui.NewGui(gocui.NewGuiOpts{
|
||||
OutputMode: gocui.OutputTrue,
|
||||
RuneReplacements: map[rune]string{},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -273,27 +286,33 @@ func (gui *Gui) setPanels() {
|
|||
Containers: gui.getContainersPanel(),
|
||||
Images: gui.getImagesPanel(),
|
||||
Volumes: gui.getVolumesPanel(),
|
||||
Networks: gui.getNetworksPanel(),
|
||||
Menu: gui.getMenuPanel(),
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) updateContainerDetails() error {
|
||||
return gui.DockerCommand.UpdateContainerDetails(gui.Panels.Containers.List.GetAllItems())
|
||||
return gui.DockerCommand.RefreshContainerDetails(gui.Panels.Containers.List.GetAllItems())
|
||||
}
|
||||
|
||||
func (gui *Gui) refresh() {
|
||||
go func() {
|
||||
// Refresh containers/services first, then projects (which depend on
|
||||
// container labels to discover projects).
|
||||
if err := gui.refreshContainersAndServices(); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
if err := gui.refreshProject(); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
if err := gui.refreshContainersAndServices(); err != nil {
|
||||
if err := gui.reloadVolumes(); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
if err := gui.reloadVolumes(); err != nil {
|
||||
if err := gui.reloadNetworks(); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
}()
|
||||
|
|
@ -317,7 +336,7 @@ func (gui *Gui) listenForEvents(ctx context.Context, refresh func()) {
|
|||
|
||||
outer:
|
||||
for {
|
||||
messageChan, errChan := gui.DockerCommand.Client.Events(context.Background(), dockerTypes.EventsOptions{})
|
||||
messageChan, errChan := gui.DockerCommand.Client.Events(context.Background(), events.ListOptions{})
|
||||
|
||||
if errorCount > 0 {
|
||||
select {
|
||||
|
|
@ -440,7 +459,7 @@ func (gui *Gui) ShouldRefresh(key string) bool {
|
|||
}
|
||||
|
||||
func (gui *Gui) initiallyFocusedViewName() string {
|
||||
if gui.DockerCommand.InDockerComposeProject {
|
||||
if gui.DockerCommand.IsProjectScoped() {
|
||||
return "services"
|
||||
}
|
||||
return "containers"
|
||||
|
|
@ -477,7 +496,11 @@ func (gui *Gui) monitorContainerStats(ctx context.Context) {
|
|||
// and panels to exist for us to know what keybindings there are, so we invoke
|
||||
// gocui in headless mode and create them.
|
||||
func (gui *Gui) SetupFakeGui() {
|
||||
g, err := gocui.NewGui(gocui.OutputTrue, false, gocui.NORMAL, true, map[rune]string{})
|
||||
g, err := gocui.NewGui(gocui.NewGuiOpts{
|
||||
OutputMode: gocui.OutputTrue,
|
||||
RuneReplacements: map[rune]string{},
|
||||
Headless: true,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
|
|
@ -123,37 +123,37 @@ func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
|||
type removeImageOption struct {
|
||||
description string
|
||||
command string
|
||||
configOptions dockerTypes.ImageRemoveOptions
|
||||
configOptions image.RemoveOptions
|
||||
}
|
||||
|
||||
image, err := gui.Panels.Images.GetSelectedItem()
|
||||
img, err := gui.Panels.Images.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
shortSha := image.ID[7:17]
|
||||
shortSha := img.ID[7:17]
|
||||
|
||||
// TODO: have a way of toggling in a menu instead of showing each permutation as a separate menu item
|
||||
options := []*removeImageOption{
|
||||
{
|
||||
description: gui.Tr.Remove,
|
||||
command: "docker image rm " + shortSha,
|
||||
configOptions: dockerTypes.ImageRemoveOptions{PruneChildren: true, Force: false},
|
||||
configOptions: image.RemoveOptions{PruneChildren: true, Force: false},
|
||||
},
|
||||
{
|
||||
description: gui.Tr.RemoveWithoutPrune,
|
||||
command: "docker image rm --no-prune " + shortSha,
|
||||
configOptions: dockerTypes.ImageRemoveOptions{PruneChildren: false, Force: false},
|
||||
configOptions: image.RemoveOptions{PruneChildren: false, Force: false},
|
||||
},
|
||||
{
|
||||
description: gui.Tr.RemoveWithForce,
|
||||
command: "docker image rm --force " + shortSha,
|
||||
configOptions: dockerTypes.ImageRemoveOptions{PruneChildren: true, Force: true},
|
||||
configOptions: image.RemoveOptions{PruneChildren: true, Force: true},
|
||||
},
|
||||
{
|
||||
description: gui.Tr.RemoveWithoutPruneWithForce,
|
||||
command: "docker image rm --no-prune --force " + shortSha,
|
||||
configOptions: dockerTypes.ImageRemoveOptions{PruneChildren: false, Force: true},
|
||||
configOptions: image.RemoveOptions{PruneChildren: false, Force: true},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -164,7 +164,7 @@ func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
|||
color.New(color.FgRed).Sprint(option.command),
|
||||
},
|
||||
OnPress: func() error {
|
||||
if err := image.Remove(option.configOptions); err != nil {
|
||||
if err := img.Remove(option.configOptions); err != nil {
|
||||
return gui.createErrorPanel(err.Error())
|
||||
}
|
||||
|
||||
|
|
@ -192,13 +192,13 @@ func (gui *Gui) handlePruneImages() error {
|
|||
}
|
||||
|
||||
func (gui *Gui) handleImagesCustomCommand(g *gocui.Gui, v *gocui.View) error {
|
||||
image, err := gui.Panels.Images.GetSelectedItem()
|
||||
img, err := gui.Panels.Images.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
|
||||
Image: image,
|
||||
Image: img,
|
||||
})
|
||||
|
||||
customCommands := gui.Config.UserConfig.CustomCommands.Images
|
||||
|
|
|
|||
|
|
@ -409,6 +409,27 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||
Handler: gui.handleVolumesBulkCommand,
|
||||
Description: gui.Tr.ViewBulkCommands,
|
||||
},
|
||||
{
|
||||
ViewName: "networks",
|
||||
Key: 'c',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleNetworksCustomCommand,
|
||||
Description: gui.Tr.RunCustomCommand,
|
||||
},
|
||||
{
|
||||
ViewName: "networks",
|
||||
Key: 'd',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleNetworksRemoveMenu,
|
||||
Description: gui.Tr.RemoveNetwork,
|
||||
},
|
||||
{
|
||||
ViewName: "networks",
|
||||
Key: 'b',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleNetworksBulkCommand,
|
||||
Description: gui.Tr.ViewBulkCommands,
|
||||
},
|
||||
{
|
||||
ViewName: "main",
|
||||
Key: gocui.KeyEsc,
|
||||
|
|
@ -513,6 +534,15 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||
}...)
|
||||
}
|
||||
|
||||
bindings = append(bindings, []*Binding{
|
||||
{Handler: gui.handleGoTo(gui.Panels.Projects.View), Key: '1', Description: gui.Tr.FocusProjects},
|
||||
{Handler: gui.handleGoTo(gui.Panels.Services.View), Key: '2', Description: gui.Tr.FocusServices},
|
||||
{Handler: gui.handleGoTo(gui.Panels.Containers.View), Key: '3', Description: gui.Tr.FocusContainers},
|
||||
{Handler: gui.handleGoTo(gui.Panels.Images.View), Key: '4', Description: gui.Tr.FocusImages},
|
||||
{Handler: gui.handleGoTo(gui.Panels.Volumes.View), Key: '5', Description: gui.Tr.FocusVolumes},
|
||||
{Handler: gui.handleGoTo(gui.Panels.Networks.View), Key: '6', Description: gui.Tr.FocusNetworks},
|
||||
}...)
|
||||
|
||||
for _, panel := range gui.allListPanels() {
|
||||
setUpDownClickBindings(panel.GetView().Name(), panel.HandlePrevLine, panel.HandleNextLine, panel.HandleClick)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,9 +120,11 @@ func (gui *Gui) focusPointInView(view *gocui.View) {
|
|||
return
|
||||
}
|
||||
|
||||
currentPanel, ok := gui.currentListPanel()
|
||||
if ok {
|
||||
currentPanel.Refocus()
|
||||
for _, panel := range gui.allListPanels() {
|
||||
if panel.GetView() == view {
|
||||
panel.Refocus()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
179
pkg/gui/networks_panel.go
Normal file
179
pkg/gui/networks_panel.go
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
package gui
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||
"github.com/jesseduffield/lazydocker/pkg/gui/panels"
|
||||
"github.com/jesseduffield/lazydocker/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazydocker/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazydocker/pkg/tasks"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func (gui *Gui) getNetworksPanel() *panels.SideListPanel[*commands.Network] {
|
||||
return &panels.SideListPanel[*commands.Network]{
|
||||
ContextState: &panels.ContextState[*commands.Network]{
|
||||
GetMainTabs: func() []panels.MainTab[*commands.Network] {
|
||||
return []panels.MainTab[*commands.Network]{
|
||||
{
|
||||
Key: "config",
|
||||
Title: gui.Tr.ConfigTitle,
|
||||
Render: gui.renderNetworkConfig,
|
||||
},
|
||||
}
|
||||
},
|
||||
GetItemContextCacheKey: func(network *commands.Network) string {
|
||||
return "networks-" + network.Name
|
||||
},
|
||||
},
|
||||
ListPanel: panels.ListPanel[*commands.Network]{
|
||||
List: panels.NewFilteredList[*commands.Network](),
|
||||
View: gui.Views.Networks,
|
||||
},
|
||||
NoItemsMessage: gui.Tr.NoNetworks,
|
||||
Gui: gui.intoInterface(),
|
||||
// we're sorting these networks based on whether they have labels defined,
|
||||
// because those are the ones you typically care about.
|
||||
// Within that, we also sort them alphabetically
|
||||
Sort: func(a *commands.Network, b *commands.Network) bool {
|
||||
return a.Name < b.Name
|
||||
},
|
||||
GetTableCells: presentation.GetNetworkDisplayStrings,
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) renderNetworkConfig(network *commands.Network) tasks.TaskFunc {
|
||||
return gui.NewSimpleRenderStringTask(func() string { return gui.networkConfigStr(network) })
|
||||
}
|
||||
|
||||
func (gui *Gui) networkConfigStr(network *commands.Network) string {
|
||||
padding := 15
|
||||
output := ""
|
||||
output += utils.WithPadding("ID: ", padding) + network.Network.ID + "\n"
|
||||
output += utils.WithPadding("Name: ", padding) + network.Name + "\n"
|
||||
output += utils.WithPadding("Driver: ", padding) + network.Network.Driver + "\n"
|
||||
output += utils.WithPadding("Scope: ", padding) + network.Network.Scope + "\n"
|
||||
output += utils.WithPadding("EnabledIPV6: ", padding) + strconv.FormatBool(network.Network.EnableIPv6) + "\n"
|
||||
output += utils.WithPadding("Internal: ", padding) + strconv.FormatBool(network.Network.Internal) + "\n"
|
||||
output += utils.WithPadding("Attachable: ", padding) + strconv.FormatBool(network.Network.Attachable) + "\n"
|
||||
output += utils.WithPadding("Ingress: ", padding) + strconv.FormatBool(network.Network.Ingress) + "\n"
|
||||
|
||||
output += utils.WithPadding("Containers: ", padding)
|
||||
if len(network.Network.Containers) > 0 {
|
||||
output += "\n"
|
||||
for _, v := range network.Network.Containers {
|
||||
output += utils.FormatMapItem(padding, v.Name, v.EndpointID)
|
||||
}
|
||||
} else {
|
||||
output += "none\n"
|
||||
}
|
||||
|
||||
output += "\n"
|
||||
output += utils.WithPadding("Labels: ", padding) + utils.FormatMap(padding, network.Network.Labels) + "\n"
|
||||
output += utils.WithPadding("Options: ", padding) + utils.FormatMap(padding, network.Network.Options)
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func (gui *Gui) reloadNetworks() error {
|
||||
if err := gui.refreshStateNetworks(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.Panels.Networks.RerenderList()
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshStateNetworks() error {
|
||||
networks, err := gui.DockerCommand.RefreshNetworks()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gui.Panels.Networks.SetItems(networks)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleNetworksRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
network, err := gui.Panels.Networks.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
type removeNetworkOption struct {
|
||||
description string
|
||||
command string
|
||||
}
|
||||
|
||||
options := []*removeNetworkOption{
|
||||
{
|
||||
description: gui.Tr.Remove,
|
||||
command: utils.WithShortSha("docker network rm " + network.Name),
|
||||
},
|
||||
}
|
||||
|
||||
menuItems := lo.Map(options, func(option *removeNetworkOption, _ int) *types.MenuItem {
|
||||
return &types.MenuItem{
|
||||
LabelColumns: []string{option.description, color.New(color.FgRed).Sprint(option.command)},
|
||||
OnPress: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
||||
if err := network.Remove(); err != nil {
|
||||
return gui.createErrorPanel(err.Error())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
return gui.Menu(CreateMenuOptions{
|
||||
Title: "",
|
||||
Items: menuItems,
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handlePruneNetworks() error {
|
||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmPruneNetworks, func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error {
|
||||
err := gui.DockerCommand.PruneNetworks()
|
||||
if err != nil {
|
||||
return gui.createErrorPanel(err.Error())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}, nil)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleNetworksCustomCommand(g *gocui.Gui, v *gocui.View) error {
|
||||
network, err := gui.Panels.Networks.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
|
||||
Network: network,
|
||||
})
|
||||
|
||||
customCommands := gui.Config.UserConfig.CustomCommands.Networks
|
||||
|
||||
return gui.createCustomCommandMenu(customCommands, commandObject)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleNetworksBulkCommand(g *gocui.Gui, v *gocui.View) error {
|
||||
baseBulkCommands := []config.CustomCommand{
|
||||
{
|
||||
Name: gui.Tr.PruneNetworks,
|
||||
InternalFunction: gui.handlePruneNetworks,
|
||||
},
|
||||
}
|
||||
|
||||
bulkCommands := append(baseBulkCommands, gui.Config.UserConfig.BulkCommands.Networks...)
|
||||
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{})
|
||||
|
||||
return gui.createBulkCommandMenu(bulkCommands, commandObject)
|
||||
}
|
||||
|
|
@ -47,6 +47,9 @@ type SideListPanel[T comparable] struct {
|
|||
// a callback to invoke when the item is clicked
|
||||
OnClick func(T) error
|
||||
|
||||
// a callback to invoke when a new item is selected (e.g. keyboard navigation)
|
||||
OnSelect func(T) error
|
||||
|
||||
// returns the cells that we render to the view in a table format. The cells will
|
||||
// be rendered with padding.
|
||||
GetTableCells func(T) []string
|
||||
|
|
@ -116,6 +119,12 @@ func (self *SideListPanel[T]) HandleSelect() error {
|
|||
|
||||
self.Refocus()
|
||||
|
||||
if self.OnSelect != nil {
|
||||
if err := self.OnSelect(item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return self.renderContext(item)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package presentation
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
|
|
@ -38,7 +37,7 @@ func RenderStats(userConfig *config.UserConfig, container *commands.Container, v
|
|||
dataReceived := fmt.Sprintf("Traffic received: %s", utils.FormatDecimalBytes(stats.ClientStats.Networks.Eth0.RxBytes))
|
||||
dataSent := fmt.Sprintf("Traffic sent: %s", utils.FormatDecimalBytes(stats.ClientStats.Networks.Eth0.TxBytes))
|
||||
|
||||
originalJSON, err := json.MarshalIndent(stats, "", " ")
|
||||
originalStats, err := utils.MarshalIntoYaml(stats)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
@ -48,7 +47,7 @@ func RenderStats(userConfig *config.UserConfig, container *commands.Container, v
|
|||
pidsCount,
|
||||
dataReceived,
|
||||
dataSent,
|
||||
string(originalJSON),
|
||||
utils.ColoredYamlString(string(originalStats)),
|
||||
)
|
||||
|
||||
return contents, nil
|
||||
|
|
|
|||
|
|
@ -6,17 +6,18 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func GetContainerDisplayStrings(container *commands.Container) []string {
|
||||
func GetContainerDisplayStrings(guiConfig *config.GuiConfig, container *commands.Container) []string {
|
||||
return []string{
|
||||
getContainerDisplayStatus(container),
|
||||
getContainerDisplaySubstatus(container),
|
||||
getContainerDisplayStatus(guiConfig, container),
|
||||
getContainerDisplaySubstatus(guiConfig, container),
|
||||
container.Name,
|
||||
getDisplayCPUPerc(container),
|
||||
utils.ColoredString(displayPorts(container), color.FgYellow),
|
||||
|
|
@ -29,7 +30,7 @@ func displayContainerImage(container *commands.Container) string {
|
|||
}
|
||||
|
||||
func displayPorts(c *commands.Container) string {
|
||||
portStrings := lo.Map(c.Container.Ports, func(port dockerTypes.Port, _ int) string {
|
||||
portStrings := lo.Map(c.Container.Ports, func(port container.Port, _ int) string {
|
||||
if port.PublicPort == 0 {
|
||||
return fmt.Sprintf("%d/%s", port.PrivatePort, port.Type)
|
||||
}
|
||||
|
|
@ -52,12 +53,44 @@ func displayPorts(c *commands.Container) string {
|
|||
}
|
||||
|
||||
// getContainerDisplayStatus returns the colored status of the container
|
||||
func getContainerDisplayStatus(c *commands.Container) string {
|
||||
return utils.ColoredString(c.Container.State, getContainerColor(c))
|
||||
func getContainerDisplayStatus(guiConfig *config.GuiConfig, c *commands.Container) string {
|
||||
shortStatusMap := map[string]string{
|
||||
"paused": "P",
|
||||
"exited": "X",
|
||||
"created": "C",
|
||||
"removing": "RM",
|
||||
"restarting": "RS",
|
||||
"running": "R",
|
||||
"dead": "D",
|
||||
}
|
||||
|
||||
iconStatusMap := map[string]rune{
|
||||
"paused": '◫',
|
||||
"exited": '⨯',
|
||||
"created": '+',
|
||||
"removing": '−',
|
||||
"restarting": '⟳',
|
||||
"running": '▶',
|
||||
"dead": '!',
|
||||
}
|
||||
|
||||
var containerState string
|
||||
switch guiConfig.ContainerStatusHealthStyle {
|
||||
case "short":
|
||||
containerState = shortStatusMap[c.Container.State]
|
||||
case "icon":
|
||||
containerState = string(iconStatusMap[c.Container.State])
|
||||
case "long":
|
||||
fallthrough
|
||||
default:
|
||||
containerState = c.Container.State
|
||||
}
|
||||
|
||||
return utils.ColoredString(containerState, getContainerColor(c))
|
||||
}
|
||||
|
||||
// GetDisplayStatus returns the exit code if the container has exited, and the health status if the container is running (and has a health check)
|
||||
func getContainerDisplaySubstatus(c *commands.Container) string {
|
||||
func getContainerDisplaySubstatus(guiConfig *config.GuiConfig, c *commands.Container) string {
|
||||
if !c.DetailsLoaded() {
|
||||
return ""
|
||||
}
|
||||
|
|
@ -68,13 +101,13 @@ func getContainerDisplaySubstatus(c *commands.Container) string {
|
|||
fmt.Sprintf("(%s)", strconv.Itoa(c.Details.State.ExitCode)), getContainerColor(c),
|
||||
)
|
||||
case "running":
|
||||
return getHealthStatus(c)
|
||||
return getHealthStatus(guiConfig, c)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func getHealthStatus(c *commands.Container) string {
|
||||
func getHealthStatus(guiConfig *config.GuiConfig, c *commands.Container) string {
|
||||
if !c.DetailsLoaded() {
|
||||
return ""
|
||||
}
|
||||
|
|
@ -88,8 +121,32 @@ func getHealthStatus(c *commands.Container) string {
|
|||
if c.Details.State.Health == nil {
|
||||
return ""
|
||||
}
|
||||
healthStatus := c.Details.State.Health.Status
|
||||
if healthStatusColor, ok := healthStatusColorMap[healthStatus]; ok {
|
||||
|
||||
shortHealthStatusMap := map[string]string{
|
||||
"healthy": "H",
|
||||
"unhealthy": "U",
|
||||
"starting": "S",
|
||||
}
|
||||
|
||||
iconHealthStatusMap := map[string]rune{
|
||||
"healthy": '✔',
|
||||
"unhealthy": '?',
|
||||
"starting": '…',
|
||||
}
|
||||
|
||||
var healthStatus string
|
||||
switch guiConfig.ContainerStatusHealthStyle {
|
||||
case "short":
|
||||
healthStatus = shortHealthStatusMap[c.Details.State.Health.Status]
|
||||
case "icon":
|
||||
healthStatus = string(iconHealthStatusMap[c.Details.State.Health.Status])
|
||||
case "long":
|
||||
fallthrough
|
||||
default:
|
||||
healthStatus = c.Details.State.Health.Status
|
||||
}
|
||||
|
||||
if healthStatusColor, ok := healthStatusColorMap[c.Details.State.Health.Status]; ok {
|
||||
return utils.ColoredString(fmt.Sprintf("(%s)", healthStatus), healthStatusColor)
|
||||
}
|
||||
return ""
|
||||
|
|
|
|||
7
pkg/gui/presentation/networks.go
Normal file
7
pkg/gui/presentation/networks.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package presentation
|
||||
|
||||
import "github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
|
||||
func GetNetworkDisplayStrings(network *commands.Network) []string {
|
||||
return []string{network.Network.Driver, network.Name}
|
||||
}
|
||||
|
|
@ -3,13 +3,26 @@ package presentation
|
|||
import (
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
)
|
||||
|
||||
func GetServiceDisplayStrings(service *commands.Service) []string {
|
||||
func GetServiceDisplayStrings(guiConfig *config.GuiConfig, service *commands.Service) []string {
|
||||
if service.Container == nil {
|
||||
var containerState string
|
||||
switch guiConfig.ContainerStatusHealthStyle {
|
||||
case "short":
|
||||
containerState = "n"
|
||||
case "icon":
|
||||
containerState = "."
|
||||
case "long":
|
||||
fallthrough
|
||||
default:
|
||||
containerState = "none"
|
||||
}
|
||||
|
||||
return []string{
|
||||
utils.ColoredString("none", color.FgBlue),
|
||||
utils.ColoredString(containerState, color.FgBlue),
|
||||
"",
|
||||
service.Name,
|
||||
"",
|
||||
|
|
@ -20,8 +33,8 @@ func GetServiceDisplayStrings(service *commands.Service) []string {
|
|||
|
||||
container := service.Container
|
||||
return []string{
|
||||
getContainerDisplayStatus(container),
|
||||
getContainerDisplaySubstatus(container),
|
||||
getContainerDisplayStatus(guiConfig, container),
|
||||
getContainerDisplaySubstatus(guiConfig, container),
|
||||
service.Name,
|
||||
getDisplayCPUPerc(container),
|
||||
utils.ColoredString(displayPorts(container), color.FgYellow),
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package gui
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
|
|
@ -16,34 +15,21 @@ import (
|
|||
"github.com/jesseduffield/yaml"
|
||||
)
|
||||
|
||||
// Although at the moment we'll only have one project, in future we could have
|
||||
// a list of projects in the project panel.
|
||||
|
||||
func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
|
||||
return &panels.SideListPanel[*commands.Project]{
|
||||
ContextState: &panels.ContextState[*commands.Project]{
|
||||
GetMainTabs: func() []panels.MainTab[*commands.Project] {
|
||||
if gui.DockerCommand.InDockerComposeProject {
|
||||
return []panels.MainTab[*commands.Project]{
|
||||
{
|
||||
Key: "logs",
|
||||
Title: gui.Tr.LogsTitle,
|
||||
Render: gui.renderAllLogs,
|
||||
},
|
||||
{
|
||||
Key: "config",
|
||||
Title: gui.Tr.DockerComposeConfigTitle,
|
||||
Render: gui.renderDockerComposeConfig,
|
||||
},
|
||||
{
|
||||
Key: "credits",
|
||||
Title: gui.Tr.CreditsTitle,
|
||||
Render: gui.renderCredits,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return []panels.MainTab[*commands.Project]{
|
||||
{
|
||||
Key: "logs",
|
||||
Title: gui.Tr.LogsTitle,
|
||||
Render: gui.renderAllLogs,
|
||||
},
|
||||
{
|
||||
Key: "config",
|
||||
Title: gui.Tr.DockerComposeConfigTitle,
|
||||
Render: gui.renderDockerComposeConfig,
|
||||
},
|
||||
{
|
||||
Key: "credits",
|
||||
Title: gui.Tr.CreditsTitle,
|
||||
|
|
@ -64,31 +50,91 @@ func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
|
|||
Gui: gui.intoInterface(),
|
||||
|
||||
Sort: func(a *commands.Project, b *commands.Project) bool {
|
||||
return false
|
||||
return a.Name < b.Name
|
||||
},
|
||||
GetTableCells: presentation.GetProjectDisplayStrings,
|
||||
// It doesn't make sense to filter a list of only one item.
|
||||
DisableFilter: true,
|
||||
OnSelect: func(project *commands.Project) error {
|
||||
// When a different project is selected, re-filter services and
|
||||
// containers to show only those belonging to the selected project.
|
||||
return gui.renderContainersAndServices()
|
||||
},
|
||||
Hide: func() bool {
|
||||
return !gui.DockerCommand.IsProjectScoped()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshProject() error {
|
||||
gui.Panels.Projects.SetItems([]*commands.Project{{Name: gui.getProjectName()}})
|
||||
return gui.Panels.Projects.RerenderList()
|
||||
}
|
||||
projects := gui.getDiscoveredProjects()
|
||||
|
||||
func (gui *Gui) getProjectName() string {
|
||||
projectName := path.Base(gui.Config.ProjectDir)
|
||||
if gui.DockerCommand.InDockerComposeProject {
|
||||
for _, service := range gui.Panels.Services.List.GetAllItems() {
|
||||
container := service.Container
|
||||
if container != nil && container.DetailsLoaded() {
|
||||
return container.Details.Config.Labels["com.docker.compose.project"]
|
||||
// Preserve the current selection across refreshes. On the first refresh,
|
||||
// select the project specified via -p flag, or fall back to the local project.
|
||||
selectedName := gui.getSelectedProjectName()
|
||||
if selectedName == "" {
|
||||
if gui.Config.ProjectName != "" {
|
||||
selectedName = gui.Config.ProjectName
|
||||
} else {
|
||||
selectedName = gui.DockerCommand.LocalProjectName
|
||||
}
|
||||
}
|
||||
|
||||
gui.Panels.Projects.SetItems(projects)
|
||||
|
||||
if selectedName != "" {
|
||||
for i, p := range gui.Panels.Projects.List.GetItems() {
|
||||
if p.Name == selectedName {
|
||||
gui.Panels.Projects.SetSelectedLineIdx(i)
|
||||
gui.Panels.Projects.Refocus()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return projectName
|
||||
return gui.Panels.Projects.RerenderList()
|
||||
}
|
||||
|
||||
// getDiscoveredProjects returns all docker compose projects by examining container labels.
|
||||
// The local project (from docker-compose.yml in the current directory, or from -p) is
|
||||
// included even when it has no running containers, so the user always sees the project
|
||||
// they explicitly scoped to.
|
||||
func (gui *Gui) getDiscoveredProjects() []*commands.Project {
|
||||
containers := gui.Panels.Containers.List.GetAllItems()
|
||||
projectNames := gui.DockerCommand.GetProjectNames(containers)
|
||||
|
||||
// If we're scoped to a project but it has no running containers, still
|
||||
// include it. We don't fall back to the directory name here to avoid
|
||||
// briefly flashing the wrong project name on startup.
|
||||
localName := gui.DockerCommand.LocalProjectName
|
||||
|
||||
if gui.DockerCommand.IsProjectScoped() && localName != "" {
|
||||
found := false
|
||||
for _, name := range projectNames {
|
||||
if name == localName {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
projectNames = append([]string{localName}, projectNames...)
|
||||
}
|
||||
}
|
||||
|
||||
projects := make([]*commands.Project, len(projectNames))
|
||||
for i, name := range projectNames {
|
||||
projects[i] = &commands.Project{Name: name}
|
||||
}
|
||||
|
||||
return projects
|
||||
}
|
||||
|
||||
// getSelectedProjectName returns the name of the currently selected project,
|
||||
// or empty string if none is selected.
|
||||
func (gui *Gui) getSelectedProjectName() string {
|
||||
project, err := gui.Panels.Projects.GetSelectedItem()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return project.Name
|
||||
}
|
||||
|
||||
func (gui *Gui) renderCredits(_project *commands.Project) tasks.TaskFunc {
|
||||
|
|
@ -108,11 +154,11 @@ func (gui *Gui) creditsStr() string {
|
|||
"Raise an Issue: https://github.com/jesseduffield/lazydocker/issues",
|
||||
utils.ColoredString("Buy Jesse a coffee: https://github.com/sponsors/jesseduffield", color.FgMagenta), // caffeine ain't free
|
||||
"Here's your lazydocker config when merged in with the defaults (you can open your config by pressing 'o'):",
|
||||
configBuf.String(),
|
||||
utils.ColoredYamlString(configBuf.String()),
|
||||
}, "\n\n")
|
||||
}
|
||||
|
||||
func (gui *Gui) renderAllLogs(_project *commands.Project) tasks.TaskFunc {
|
||||
func (gui *Gui) renderAllLogs(project *commands.Project) tasks.TaskFunc {
|
||||
return gui.NewTask(TaskOpts{
|
||||
Autoscroll: true,
|
||||
Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
|
||||
|
|
@ -122,7 +168,7 @@ func (gui *Gui) renderAllLogs(_project *commands.Project) tasks.TaskFunc {
|
|||
cmd := gui.OSCommand.RunCustomCommand(
|
||||
utils.ApplyTemplate(
|
||||
gui.Config.UserConfig.CommandTemplates.AllLogs,
|
||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
|
||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{Project: project}),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -144,8 +190,20 @@ func (gui *Gui) renderAllLogs(_project *commands.Project) tasks.TaskFunc {
|
|||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) renderDockerComposeConfig(_project *commands.Project) tasks.TaskFunc {
|
||||
return gui.NewSimpleRenderStringTask(func() string { return gui.DockerCommand.DockerComposeConfig() })
|
||||
func (gui *Gui) renderDockerComposeConfig(project *commands.Project) tasks.TaskFunc {
|
||||
if !gui.DockerCommand.InDockerComposeProject {
|
||||
return gui.NewSimpleRenderStringTask(func() string {
|
||||
return "Compose config is only available when launched from a docker-compose project directory"
|
||||
})
|
||||
}
|
||||
if project != nil && project.Name != gui.DockerCommand.LocalProjectName {
|
||||
return gui.NewSimpleRenderStringTask(func() string {
|
||||
return "Compose config is not available for non-local projects"
|
||||
})
|
||||
}
|
||||
return gui.NewSimpleRenderStringTask(func() string {
|
||||
return utils.ColoredYamlString(gui.DockerCommand.DockerComposeConfigForProject(project))
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleOpenConfig(g *gocui.Gui, v *gocui.View) error {
|
||||
|
|
@ -171,7 +229,8 @@ func lazydockerTitle() string {
|
|||
|
||||
// handleViewAllLogs switches to a subprocess viewing all the logs from docker-compose
|
||||
func (gui *Gui) handleViewAllLogs(g *gocui.Gui, v *gocui.View) error {
|
||||
c, err := gui.DockerCommand.ViewAllLogs()
|
||||
project, _ := gui.Panels.Projects.GetSelectedItem()
|
||||
c, err := gui.DockerCommand.ViewAllLogs(project)
|
||||
if err != nil {
|
||||
return gui.createErrorPanel(err.Error())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,9 +74,23 @@ func (gui *Gui) getServicesPanel() *panels.SideListPanel[*commands.Service] {
|
|||
|
||||
return a.Name < b.Name
|
||||
},
|
||||
GetTableCells: presentation.GetServiceDisplayStrings,
|
||||
Filter: func(service *commands.Service) bool {
|
||||
selectedProject := gui.getSelectedProjectName()
|
||||
if selectedProject == "" {
|
||||
// Before any project is selected (e.g. startup), default to
|
||||
// the local project so we don't briefly flash all services.
|
||||
selectedProject = gui.DockerCommand.LocalProjectName
|
||||
}
|
||||
if selectedProject == "" {
|
||||
return true
|
||||
}
|
||||
return service.ProjectName == selectedProject
|
||||
},
|
||||
GetTableCells: func(service *commands.Service) []string {
|
||||
return presentation.GetServiceDisplayStrings(&gui.Config.UserConfig.Gui, service)
|
||||
},
|
||||
Hide: func() bool {
|
||||
return !gui.DockerCommand.InDockerComposeProject
|
||||
return !gui.DockerCommand.IsProjectScoped()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -140,13 +154,24 @@ func (r *commandOption) getDisplayStrings() []string {
|
|||
return []string{r.description, color.New(color.FgCyan).Sprint(r.command)}
|
||||
}
|
||||
|
||||
// isServiceFromLocalProject returns true if the given service belongs to the
|
||||
// local compose project (the one whose compose file is in the current directory).
|
||||
// Compose commands like up/stop/restart only work for local project services.
|
||||
func (gui *Gui) isServiceFromLocalProject(service *commands.Service) bool {
|
||||
return service.ProjectName == gui.DockerCommand.LocalProjectName
|
||||
}
|
||||
|
||||
func (gui *Gui) handleServiceRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
service, err := gui.Panels.Services.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
composeCommand := gui.Config.UserConfig.CommandTemplates.DockerCompose
|
||||
if !gui.isServiceFromLocalProject(service) {
|
||||
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||
}
|
||||
|
||||
composeCommand := gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}).DockerCompose
|
||||
|
||||
options := []*commandOption{
|
||||
{
|
||||
|
|
@ -200,10 +225,15 @@ func (gui *Gui) handleServiceStop(g *gocui.Gui, v *gocui.View) error {
|
|||
|
||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.StopService, func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
|
||||
if !gui.isServiceFromLocalProject(service) {
|
||||
if service.Container == nil {
|
||||
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||
}
|
||||
return service.Container.Stop()
|
||||
}
|
||||
if err := service.Stop(); err != nil {
|
||||
return gui.createErrorPanel(err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}, nil)
|
||||
|
|
@ -215,6 +245,10 @@ func (gui *Gui) handleServiceUp(g *gocui.Gui, v *gocui.View) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if !gui.isServiceFromLocalProject(service) {
|
||||
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.UppingServiceStatus, func() error {
|
||||
if err := service.Up(); err != nil {
|
||||
return gui.createErrorPanel(err.Error())
|
||||
|
|
@ -231,10 +265,15 @@ func (gui *Gui) handleServiceRestart(g *gocui.Gui, v *gocui.View) error {
|
|||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
|
||||
if !gui.isServiceFromLocalProject(service) {
|
||||
if service.Container == nil {
|
||||
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||
}
|
||||
return service.Container.Restart()
|
||||
}
|
||||
if err := service.Restart(); err != nil {
|
||||
return gui.createErrorPanel(err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
|
@ -245,11 +284,19 @@ func (gui *Gui) handleServiceStart(g *gocui.Gui, v *gocui.View) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if !gui.isServiceFromLocalProject(service) {
|
||||
if service.Container == nil {
|
||||
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||
}
|
||||
return gui.WithWaitingStatus(gui.Tr.StartingStatus, func() error {
|
||||
return service.Container.Start()
|
||||
})
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.StartingStatus, func() error {
|
||||
if err := service.Start(); err != nil {
|
||||
return gui.createErrorPanel(err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
|
@ -287,10 +334,14 @@ func (gui *Gui) handleServiceRenderLogsToMain(g *gocui.Gui, v *gocui.View) error
|
|||
}
|
||||
|
||||
func (gui *Gui) handleProjectUp(g *gocui.Gui, v *gocui.View) error {
|
||||
project, _ := gui.Panels.Projects.GetSelectedItem()
|
||||
if project != nil && project.Name != gui.DockerCommand.LocalProjectName {
|
||||
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||
}
|
||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmUpProject, func(g *gocui.Gui, v *gocui.View) error {
|
||||
cmdStr := utils.ApplyTemplate(
|
||||
gui.Config.UserConfig.CommandTemplates.Up,
|
||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
|
||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{Project: project}),
|
||||
)
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.UppingProjectStatus, func() error {
|
||||
|
|
@ -303,14 +354,18 @@ func (gui *Gui) handleProjectUp(g *gocui.Gui, v *gocui.View) error {
|
|||
}
|
||||
|
||||
func (gui *Gui) handleProjectDown(g *gocui.Gui, v *gocui.View) error {
|
||||
project, _ := gui.Panels.Projects.GetSelectedItem()
|
||||
if project != nil && project.Name != gui.DockerCommand.LocalProjectName {
|
||||
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||
}
|
||||
downCommand := utils.ApplyTemplate(
|
||||
gui.Config.UserConfig.CommandTemplates.Down,
|
||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
|
||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{Project: project}),
|
||||
)
|
||||
|
||||
downWithVolumesCommand := utils.ApplyTemplate(
|
||||
gui.Config.UserConfig.CommandTemplates.DownWithVolumes,
|
||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
|
||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{Project: project}),
|
||||
)
|
||||
|
||||
options := []*commandOption{
|
||||
|
|
@ -359,6 +414,10 @@ func (gui *Gui) handleServiceRestartMenu(g *gocui.Gui, v *gocui.View) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if !gui.isServiceFromLocalProject(service) {
|
||||
return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
|
||||
}
|
||||
|
||||
rebuildCommand := utils.ApplyTemplate(
|
||||
gui.Config.UserConfig.CommandTemplates.RebuildService,
|
||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}),
|
||||
|
|
@ -463,8 +522,9 @@ L:
|
|||
}
|
||||
|
||||
func (gui *Gui) handleServicesBulkCommand(g *gocui.Gui, v *gocui.View) error {
|
||||
project, _ := gui.Panels.Projects.GetSelectedItem()
|
||||
bulkCommands := gui.Config.UserConfig.BulkCommands.Services
|
||||
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{})
|
||||
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{Project: project})
|
||||
|
||||
return gui.createBulkCommandMenu(bulkCommands, commandObject)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"sort"
|
||||
"testing"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
|
@ -14,28 +14,28 @@ func sampleContainers() []*commands.Container {
|
|||
{
|
||||
ID: "1",
|
||||
Name: "1",
|
||||
Container: dockerTypes.Container{
|
||||
Container: container.Summary{
|
||||
State: "exited",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "2",
|
||||
Name: "2",
|
||||
Container: dockerTypes.Container{
|
||||
Container: container.Summary{
|
||||
State: "running",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "3",
|
||||
Name: "3",
|
||||
Container: dockerTypes.Container{
|
||||
Container: container.Summary{
|
||||
State: "running",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "4",
|
||||
Name: "4",
|
||||
Container: dockerTypes.Container{
|
||||
Container: container.Summary{
|
||||
State: "created",
|
||||
},
|
||||
},
|
||||
|
|
@ -47,28 +47,28 @@ func expectedPerStatusContainers() []*commands.Container {
|
|||
{
|
||||
ID: "2",
|
||||
Name: "2",
|
||||
Container: dockerTypes.Container{
|
||||
Container: container.Summary{
|
||||
State: "running",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "3",
|
||||
Name: "3",
|
||||
Container: dockerTypes.Container{
|
||||
Container: container.Summary{
|
||||
State: "running",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "1",
|
||||
Name: "1",
|
||||
Container: dockerTypes.Container{
|
||||
Container: container.Summary{
|
||||
State: "exited",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "4",
|
||||
Name: "4",
|
||||
Container: dockerTypes.Container{
|
||||
Container: container.Summary{
|
||||
State: "created",
|
||||
},
|
||||
},
|
||||
|
|
@ -80,28 +80,28 @@ func expectedLegacySortedContainers() []*commands.Container {
|
|||
{
|
||||
ID: "1",
|
||||
Name: "1",
|
||||
Container: dockerTypes.Container{
|
||||
Container: container.Summary{
|
||||
State: "exited",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "2",
|
||||
Name: "2",
|
||||
Container: dockerTypes.Container{
|
||||
Container: container.Summary{
|
||||
State: "running",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "3",
|
||||
Name: "3",
|
||||
Container: dockerTypes.Container{
|
||||
Container: container.Summary{
|
||||
State: "running",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "4",
|
||||
Name: "4",
|
||||
Container: dockerTypes.Container{
|
||||
Container: container.Summary{
|
||||
State: "created",
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package gui
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
|
|
@ -13,6 +13,10 @@ import (
|
|||
)
|
||||
|
||||
func (gui *Gui) runSubprocess(cmd *exec.Cmd) error {
|
||||
return gui.runSubprocessWithMessage(cmd, "")
|
||||
}
|
||||
|
||||
func (gui *Gui) runSubprocessWithMessage(cmd *exec.Cmd, msg string) error {
|
||||
gui.Mutexes.SubprocessMutex.Lock()
|
||||
defer gui.Mutexes.SubprocessMutex.Unlock()
|
||||
|
||||
|
|
@ -22,7 +26,7 @@ func (gui *Gui) runSubprocess(cmd *exec.Cmd) error {
|
|||
|
||||
gui.PauseBackgroundThreads = true
|
||||
|
||||
gui.runCommand(cmd)
|
||||
gui.runCommand(cmd, msg)
|
||||
|
||||
if err := gui.g.Resume(); err != nil {
|
||||
return gui.createErrorPanel(err.Error())
|
||||
|
|
@ -33,7 +37,7 @@ func (gui *Gui) runSubprocess(cmd *exec.Cmd) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) runCommand(cmd *exec.Cmd) {
|
||||
func (gui *Gui) runCommand(cmd *exec.Cmd, msg string) {
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stdout
|
||||
cmd.Stdin = os.Stdin
|
||||
|
|
@ -51,7 +55,9 @@ func (gui *Gui) runCommand(cmd *exec.Cmd) {
|
|||
}()
|
||||
|
||||
fmt.Fprintf(os.Stdout, "\n%s\n\n", utils.ColoredString("+ "+strings.Join(cmd.Args, " "), color.FgBlue))
|
||||
|
||||
if msg != "" {
|
||||
fmt.Fprintf(os.Stdout, "\n%s\n\n", utils.ColoredString(msg, color.FgGreen))
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
// not handling the error explicitly because usually we're going to see it
|
||||
// in the output anyway
|
||||
|
|
@ -59,8 +65,8 @@ func (gui *Gui) runCommand(cmd *exec.Cmd) {
|
|||
}
|
||||
|
||||
cmd.Stdin = nil
|
||||
cmd.Stdout = ioutil.Discard
|
||||
cmd.Stderr = ioutil.Discard
|
||||
cmd.Stdout = io.Discard
|
||||
cmd.Stderr = io.Discard
|
||||
|
||||
gui.promptToReturn()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,13 @@ import (
|
|||
"github.com/spkg/bom"
|
||||
)
|
||||
|
||||
func (gui *Gui) handleGoTo(view *gocui.View) func(g *gocui.Gui, v *gocui.View) error {
|
||||
return func(g *gocui.Gui, v *gocui.View) error {
|
||||
gui.resetMainView()
|
||||
return gui.switchFocus(view)
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) nextView(g *gocui.Gui, v *gocui.View) error {
|
||||
sideViewNames := gui.sideViewNames()
|
||||
var focusedViewName string
|
||||
|
|
@ -362,6 +369,7 @@ func (gui *Gui) allSidePanels() []panels.ISideListPanel {
|
|||
gui.Panels.Containers,
|
||||
gui.Panels.Images,
|
||||
gui.Panels.Volumes,
|
||||
gui.Panels.Networks,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,30 @@
|
|||
package gui
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
// See https://github.com/xtermjs/xterm.js/issues/4238
|
||||
// VSCode is soon to fix this in an upcoming update.
|
||||
// Once that's done, we can scrap the HIDE_UNDERSCORES variable
|
||||
var (
|
||||
underscoreEnvChecked bool
|
||||
hideUnderscores bool
|
||||
)
|
||||
|
||||
func hideUnderScores() bool {
|
||||
if !underscoreEnvChecked {
|
||||
hideUnderscores = os.Getenv("TERM_PROGRAM") == "vscode"
|
||||
underscoreEnvChecked = true
|
||||
}
|
||||
|
||||
return hideUnderscores
|
||||
}
|
||||
|
||||
type Views struct {
|
||||
// side panels
|
||||
Project *gocui.View
|
||||
|
|
@ -13,6 +32,7 @@ type Views struct {
|
|||
Containers *gocui.View
|
||||
Images *gocui.View
|
||||
Volumes *gocui.View
|
||||
Networks *gocui.View
|
||||
|
||||
// main panel
|
||||
Main *gocui.View
|
||||
|
|
@ -51,6 +71,7 @@ func (gui *Gui) orderedViewNameMappings() []viewNameMapping {
|
|||
{viewPtr: &gui.Views.Containers, name: "containers", autoPosition: true},
|
||||
{viewPtr: &gui.Views.Images, name: "images", autoPosition: true},
|
||||
{viewPtr: &gui.Views.Volumes, name: "volumes", autoPosition: true},
|
||||
{viewPtr: &gui.Views.Networks, name: "networks", autoPosition: true},
|
||||
|
||||
{viewPtr: &gui.Views.Main, name: "main", autoPosition: true},
|
||||
|
||||
|
|
@ -71,43 +92,66 @@ func (gui *Gui) orderedViewNameMappings() []viewNameMapping {
|
|||
}
|
||||
|
||||
func (gui *Gui) createAllViews() error {
|
||||
frameRunes := []rune{'─', '│', '╭', '╮', '╰', '╯'}
|
||||
switch gui.Config.UserConfig.Gui.Border {
|
||||
case "single":
|
||||
frameRunes = []rune{'─', '│', '┌', '┐', '└', '┘'}
|
||||
case "double":
|
||||
frameRunes = []rune{'═', '║', '╔', '╗', '╚', '╝'}
|
||||
case "hidden":
|
||||
frameRunes = []rune{' ', ' ', ' ', ' ', ' ', ' '}
|
||||
}
|
||||
|
||||
var err error
|
||||
for _, mapping := range gui.orderedViewNameMappings() {
|
||||
*mapping.viewPtr, err = gui.prepareView(mapping.name)
|
||||
if err != nil && err.Error() != UNKNOWN_VIEW_ERROR_MSG {
|
||||
return err
|
||||
}
|
||||
(*mapping.viewPtr).FrameRunes = frameRunes
|
||||
(*mapping.viewPtr).FgColor = gocui.ColorDefault
|
||||
}
|
||||
|
||||
selectedLineBgColor := gocui.ColorBlue
|
||||
selectedLineBgColor := GetGocuiStyle(gui.Config.UserConfig.Gui.Theme.SelectedLineBgColor)
|
||||
|
||||
gui.Views.Main.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
|
||||
// when you run a docker container with the -it flags (interactive mode) it adds carriage returns for some reason. This is not docker's fault, it's an os-level default.
|
||||
gui.Views.Main.IgnoreCarriageReturns = true
|
||||
|
||||
gui.Views.Project.Title = gui.Tr.ProjectTitle
|
||||
gui.Views.Project.TitlePrefix = "[1]"
|
||||
gui.Views.Project.Highlight = true
|
||||
gui.Views.Project.SelBgColor = selectedLineBgColor
|
||||
|
||||
gui.Views.Services.Highlight = true
|
||||
gui.Views.Services.Title = gui.Tr.ServicesTitle
|
||||
gui.Views.Services.TitlePrefix = "[2]"
|
||||
gui.Views.Services.SelBgColor = selectedLineBgColor
|
||||
|
||||
gui.Views.Containers.Highlight = true
|
||||
gui.Views.Containers.SelBgColor = selectedLineBgColor
|
||||
if gui.Config.UserConfig.Gui.ShowAllContainers || !gui.DockerCommand.InDockerComposeProject {
|
||||
if gui.Config.UserConfig.Gui.ShowAllContainers || !gui.DockerCommand.IsProjectScoped() {
|
||||
gui.Views.Containers.Title = gui.Tr.ContainersTitle
|
||||
} else {
|
||||
gui.Views.Containers.Title = gui.Tr.StandaloneContainersTitle
|
||||
}
|
||||
gui.Views.Containers.TitlePrefix = "[3]"
|
||||
|
||||
gui.Views.Images.Highlight = true
|
||||
gui.Views.Images.Title = gui.Tr.ImagesTitle
|
||||
gui.Views.Images.SelBgColor = selectedLineBgColor
|
||||
gui.Views.Images.TitlePrefix = "[4]"
|
||||
|
||||
gui.Views.Volumes.Highlight = true
|
||||
gui.Views.Volumes.Title = gui.Tr.VolumesTitle
|
||||
gui.Views.Volumes.TitlePrefix = "[5]"
|
||||
gui.Views.Volumes.SelBgColor = selectedLineBgColor
|
||||
|
||||
gui.Views.Networks.Highlight = true
|
||||
gui.Views.Networks.Title = gui.Tr.NetworksTitle
|
||||
gui.Views.Networks.TitlePrefix = "[6]"
|
||||
gui.Views.Networks.SelBgColor = selectedLineBgColor
|
||||
|
||||
gui.Views.Options.Frame = false
|
||||
gui.Views.Options.FgColor = gui.GetOptionsPanelTextColor()
|
||||
|
||||
|
|
@ -155,7 +199,12 @@ func (gui *Gui) getInformationContent() string {
|
|||
return informationStr
|
||||
}
|
||||
|
||||
donate := color.New(color.FgMagenta, color.Underline).Sprint(gui.Tr.Donate)
|
||||
attrs := []color.Attribute{color.FgMagenta}
|
||||
if !hideUnderScores() {
|
||||
attrs = append(attrs, color.Underline)
|
||||
}
|
||||
|
||||
donate := color.New(attrs...).Sprint(gui.Tr.Donate)
|
||||
return donate + " " + informationStr
|
||||
}
|
||||
|
||||
|
|
|
|||
136
pkg/i18n/chinese.go
Normal file
136
pkg/i18n/chinese.go
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
package i18n
|
||||
|
||||
func chineseSet() TranslationSet {
|
||||
return TranslationSet{
|
||||
PruningStatus: "修剪中",
|
||||
RemovingStatus: "移除中",
|
||||
RestartingStatus: "重启中",
|
||||
StartingStatus: "启动中",
|
||||
StoppingStatus: "停止中",
|
||||
UppingServiceStatus: "升级服务中",
|
||||
UppingProjectStatus: "升级项目中",
|
||||
DowningStatus: "下架中",
|
||||
PausingStatus: "暂停中",
|
||||
RunningCustomCommandStatus: "正在运行自定义命令",
|
||||
RunningBulkCommandStatus: "正在运行批量命令",
|
||||
|
||||
NoViewMachingNewLineFocusedSwitchStatement: "没有匹配 newLineFocused switch 语句的视图",
|
||||
|
||||
ErrorOccurred: "发生错误!请在 https://github.com/jesseduffield/lazydocker/issues 上创建一个问题",
|
||||
ConnectionFailed: "无法连接到 Docker 客户端。您可能需要重新启动 Docker 客户端",
|
||||
UnattachableContainerError: "容器不支持 attaching。您必须使用“-it”标志运行服务,或者在docker-compose.yml文件中使用`stdin_open: true,tty: true`",
|
||||
WaitingForContainerInfo: "在 Docker 给我们更多关于容器的信息之前,无法继续。请几分钟后重试。",
|
||||
CannotAttachStoppedContainerError: "您不能 attach 到已停止的容器,您需要先启动它(您可以用 'r' 键来执行此操作)(是的,我懒得为您自动执行此操作)(很酷的是,我可以通过错误消息与您进行一对一的通讯)",
|
||||
CannotAccessDockerSocketError: "无法访问 Docker 套接字:unix:///var/run/docker.sock\n请以 root 用户身份运行 lazydocker 或阅读https://docs.docker.com/install/linux/linux-postinstall/",
|
||||
CannotKillChildError: "等待三秒钟以停止子进程。可能有一个孤儿进程在您的系统上继续运行。",
|
||||
|
||||
Donate: "捐赠",
|
||||
Confirm: "确认",
|
||||
|
||||
Return: "返回",
|
||||
FocusMain: "聚焦主面板",
|
||||
LcFilter: "过滤列表",
|
||||
Navigate: "导航",
|
||||
Execute: "执行",
|
||||
Close: "关闭",
|
||||
Quit: "退出",
|
||||
Menu: "菜单",
|
||||
MenuTitle: "菜单",
|
||||
Scroll: "滚动",
|
||||
OpenConfig: "打开lazydocker配置",
|
||||
EditConfig: "编辑lazydocker配置",
|
||||
Cancel: "取消",
|
||||
Remove: "移除",
|
||||
HideStopped: "隐藏/显示已停止的容器",
|
||||
ForceRemove: "强制移除",
|
||||
RemoveWithVolumes: "移除并删除卷",
|
||||
RemoveService: "移除容器",
|
||||
UpService: "启动服务",
|
||||
Stop: "停止",
|
||||
Pause: "暂停",
|
||||
Restart: "重新启动",
|
||||
Down: "关闭项目",
|
||||
DownWithVolumes: "关闭包括卷的项目",
|
||||
Start: "启动项目",
|
||||
Rebuild: "重建",
|
||||
Recreate: "重新创建",
|
||||
PreviousContext: "上一个选项卡",
|
||||
NextContext: "下一个选项卡",
|
||||
// Attach: "连接/附加",
|
||||
ViewLogs: "查看日志",
|
||||
UpProject: "创建并启动容器",
|
||||
DownProject: "停止并移除容器",
|
||||
RemoveImage: "移除镜像",
|
||||
RemoveVolume: "移除卷",
|
||||
RemoveNetwork: "移除网络",
|
||||
RemoveWithoutPrune: "移除但不删除未标记的父级",
|
||||
RemoveWithoutPruneWithForce: "移除(强制)但不删除未标记的父级",
|
||||
RemoveWithForce: "移除(强制)",
|
||||
PruneContainers: "删除退出的容器",
|
||||
PruneVolumes: "删除未使用的卷",
|
||||
PruneNetworks: "删除未使用的网络",
|
||||
PruneImages: "删除未使用的镜像",
|
||||
StopAllContainers: "停止所有容器",
|
||||
RemoveAllContainers: "删除所有容器(强制)",
|
||||
ViewRestartOptions: "查看重启选项",
|
||||
ExecShell: "执行shell",
|
||||
RunCustomCommand: "运行预定义的自定义命令",
|
||||
ViewBulkCommands: "查看批量命令",
|
||||
FilterList: "过滤列表",
|
||||
OpenInBrowser: "在浏览器中打开(第一个端口为http)",
|
||||
SortContainersByState: "按状态排序容器",
|
||||
|
||||
GlobalTitle: "全局",
|
||||
MainTitle: "主要",
|
||||
ProjectTitle: "项目",
|
||||
ServicesTitle: "服务",
|
||||
ContainersTitle: "容器",
|
||||
StandaloneContainersTitle: "独立容器",
|
||||
ImagesTitle: "镜像",
|
||||
VolumesTitle: "卷",
|
||||
NetworksTitle: "网络",
|
||||
CustomCommandTitle: "自定义命令:",
|
||||
BulkCommandTitle: "批量命令:",
|
||||
ErrorTitle: "错误",
|
||||
LogsTitle: "日志",
|
||||
ConfigTitle: "配置",
|
||||
EnvTitle: "环境变量",
|
||||
DockerComposeConfigTitle: "Docker-Compose配置",
|
||||
TopTitle: "系统资源管理",
|
||||
StatsTitle: "统计信息",
|
||||
CreditsTitle: "关于我们",
|
||||
ContainerConfigTitle: "容器配置",
|
||||
ContainerEnvTitle: "容器环境变量",
|
||||
NothingToDisplay: "无内容显示",
|
||||
NoContainerForService: "没有日志可以展示;该服务未关联任何容器",
|
||||
CannotDisplayEnvVariables: "展示环境变量时出现问题",
|
||||
|
||||
NoContainers: "没有容器",
|
||||
NoContainer: "没有容器",
|
||||
NoImages: "没有镜像",
|
||||
NoVolumes: "没有卷",
|
||||
NoNetworks: "没有网络",
|
||||
NoServices: "没有服务",
|
||||
|
||||
ConfirmQuit: "您确定要退出吗?",
|
||||
ConfirmUpProject: "您确定要“up”的docker compose项目吗?",
|
||||
MustForceToRemoveContainer: "您无法删除正在运行的容器,除非您强制执行。您想强制执行吗?",
|
||||
NotEnoughSpace: "空间不足,无法渲染面板",
|
||||
ConfirmPruneImages: "您确定要删除所有未使用的镜像吗?",
|
||||
ConfirmPruneContainers: "您确定要删除所有停止的容器吗?",
|
||||
ConfirmStopContainers: "您确定要停止所有容器吗?",
|
||||
ConfirmRemoveContainers: "您确定要删除所有容器吗?",
|
||||
ConfirmPruneVolumes: "您确定要删除所有未使用的卷吗?",
|
||||
ConfirmPruneNetworks: "您确定要删除所有未使用的网络吗?",
|
||||
StopService: "您确定要停止此服务的容器吗?",
|
||||
StopContainer: "您确定要停止此容器吗?",
|
||||
PressEnterToReturn: "按 enter 返回 lazydocker(您可以在配置文件中设置 `gui.returnImmediately: true` 来禁用此提示)",
|
||||
|
||||
No: "否",
|
||||
Yes: "是",
|
||||
|
||||
LcNextScreenMode: "下一个屏幕模式(正常/半屏/全屏)",
|
||||
LcPrevScreenMode: "上一个屏幕模式",
|
||||
FilterPrompt: "筛选",
|
||||
}
|
||||
}
|
||||
|
|
@ -45,9 +45,11 @@ func dutchSet() TranslationSet {
|
|||
ViewLogs: "bekijk logs",
|
||||
RemoveImage: "verwijder image",
|
||||
RemoveVolume: "verwijder volume",
|
||||
RemoveNetwork: "verwijder network",
|
||||
RemoveWithoutPrune: "verwijder zonder de ongelabeld ouders te verwijderen",
|
||||
PruneContainers: "vernietig bestaande containers",
|
||||
PruneVolumes: "vernietig ongebruikte volumes",
|
||||
PruneNetworks: "vernietig ongebruikte networks",
|
||||
PruneImages: "vernietig ongebruikte images",
|
||||
ViewRestartOptions: "bekijk herstart opties",
|
||||
RunCustomCommand: "draai een vooraf bedacht aangepaste opdracht",
|
||||
|
|
@ -60,6 +62,7 @@ func dutchSet() TranslationSet {
|
|||
StandaloneContainersTitle: "Alleenstaande Containers",
|
||||
ImagesTitle: "Images",
|
||||
VolumesTitle: "Volumes",
|
||||
NetworksTitle: "Networks",
|
||||
CustomCommandTitle: "Aangepast commando:",
|
||||
ErrorTitle: "Fout",
|
||||
LogsTitle: "Logs",
|
||||
|
|
@ -78,15 +81,18 @@ func dutchSet() TranslationSet {
|
|||
NoContainer: "Geen container",
|
||||
NoImages: "Geen images",
|
||||
NoVolumes: "Geen volumes",
|
||||
NoNetworks: "Geen networks",
|
||||
|
||||
ConfirmQuit: "Weet je zeker dat je weg wil gaan?",
|
||||
MustForceToRemoveContainer: "Je kan geen draaiende container verwijderen tenzij je het forceert, Wil je het forceren?",
|
||||
NotEnoughSpace: "Niet genoeg ruimte om de panelen te renderen",
|
||||
ConfirmPruneImages: "Weet je zeker dat je alle niet gebruikte images wil vernietigen?",
|
||||
ConfirmPruneContainers: "Weet je zeker dat je alle niet gestopte containers wil vernietigen?",
|
||||
ConfirmPruneVolumes: "Weet je zeker dat je alle niet gebruikte volumes wil vernietigen?",
|
||||
StopService: "Weet je zeker dat je deze service zijn containers wil stoppen?",
|
||||
StopContainer: "Weet je zeker dat je deze container wil stoppen?",
|
||||
PressEnterToReturn: "Druk op enter om terug te gaan naar lazydocker (Deze popup kan uit gezet worden door in de config dit neer te zetten `gui.returnImmediately: true`)",
|
||||
ConfirmQuit: "Weet je zeker dat je weg wil gaan?",
|
||||
MustForceToRemoveContainer: "Je kan geen draaiende container verwijderen tenzij je het forceert, Wil je het forceren?",
|
||||
NotEnoughSpace: "Niet genoeg ruimte om de panelen te renderen",
|
||||
ConfirmPruneImages: "Weet je zeker dat je alle niet gebruikte images wil vernietigen?",
|
||||
ConfirmPruneContainers: "Weet je zeker dat je alle niet gestopte containers wil vernietigen?",
|
||||
ConfirmPruneVolumes: "Weet je zeker dat je alle niet gebruikte volumes wil vernietigen?",
|
||||
ConfirmPruneNetworks: "Weet je zeker dat je alle niet gebruikte networks wil vernietigen?",
|
||||
StopService: "Weet je zeker dat je deze service zijn containers wil stoppen?",
|
||||
StopContainer: "Weet je zeker dat je deze container wil stoppen?",
|
||||
PressEnterToReturn: "Druk op enter om terug te gaan naar lazydocker (Deze popup kan uit gezet worden door in de config dit neer te zetten `gui.returnImmediately: true`)",
|
||||
DetachFromContainerShortCut: "Als u wilt loskoppelen van de container, drukt u standaard op ctrl-p en vervolgens op ctrl-q",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,27 +73,33 @@ type TranslationSet struct {
|
|||
TopTitle string
|
||||
ImagesTitle string
|
||||
VolumesTitle string
|
||||
NetworksTitle string
|
||||
NoContainers string
|
||||
NoContainer string
|
||||
NoImages string
|
||||
NoVolumes string
|
||||
NoNetworks string
|
||||
NoServices string
|
||||
RemoveImage string
|
||||
RemoveVolume string
|
||||
RemoveNetwork string
|
||||
RemoveWithoutPrune string
|
||||
RemoveWithoutPruneWithForce string
|
||||
RemoveWithForce string
|
||||
PruneImages string
|
||||
PruneContainers string
|
||||
PruneVolumes string
|
||||
PruneNetworks string
|
||||
ConfirmPruneContainers string
|
||||
ConfirmStopContainers string
|
||||
ConfirmRemoveContainers string
|
||||
ConfirmPruneImages string
|
||||
ConfirmPruneVolumes string
|
||||
ConfirmPruneNetworks string
|
||||
PruningStatus string
|
||||
StopService string
|
||||
PressEnterToReturn string
|
||||
DetachFromContainerShortCut string
|
||||
StopAllContainers string
|
||||
RemoveAllContainers string
|
||||
ViewRestartOptions string
|
||||
|
|
@ -104,17 +110,18 @@ type TranslationSet struct {
|
|||
OpenInBrowser string
|
||||
SortContainersByState string
|
||||
|
||||
LogsTitle string
|
||||
ConfigTitle string
|
||||
EnvTitle string
|
||||
DockerComposeConfigTitle string
|
||||
StatsTitle string
|
||||
CreditsTitle string
|
||||
ContainerConfigTitle string
|
||||
ContainerEnvTitle string
|
||||
NothingToDisplay string
|
||||
NoContainerForService string
|
||||
CannotDisplayEnvVariables string
|
||||
LogsTitle string
|
||||
ConfigTitle string
|
||||
EnvTitle string
|
||||
DockerComposeConfigTitle string
|
||||
StatsTitle string
|
||||
CreditsTitle string
|
||||
ContainerConfigTitle string
|
||||
ContainerEnvTitle string
|
||||
NothingToDisplay string
|
||||
NoContainerForService string
|
||||
CannotDisplayEnvVariables string
|
||||
CannotManageNonLocalService string
|
||||
|
||||
No string
|
||||
Yes string
|
||||
|
|
@ -122,6 +129,13 @@ type TranslationSet struct {
|
|||
LcNextScreenMode string
|
||||
LcPrevScreenMode string
|
||||
FilterPrompt string
|
||||
|
||||
FocusProjects string
|
||||
FocusServices string
|
||||
FocusContainers string
|
||||
FocusImages string
|
||||
FocusVolumes string
|
||||
FocusNetworks string
|
||||
}
|
||||
|
||||
func englishSet() TranslationSet {
|
||||
|
|
@ -186,11 +200,13 @@ func englishSet() TranslationSet {
|
|||
DownProject: "down project",
|
||||
RemoveImage: "remove image",
|
||||
RemoveVolume: "remove volume",
|
||||
RemoveNetwork: "remove network",
|
||||
RemoveWithoutPrune: "remove without deleting untagged parents",
|
||||
RemoveWithoutPruneWithForce: "remove (forced) without deleting untagged parents",
|
||||
RemoveWithForce: "remove (forced)",
|
||||
PruneContainers: "prune exited containers",
|
||||
PruneVolumes: "prune unused volumes",
|
||||
PruneNetworks: "prune unused networks",
|
||||
PruneImages: "prune unused images",
|
||||
StopAllContainers: "stop all containers",
|
||||
RemoveAllContainers: "remove all containers (forced)",
|
||||
|
|
@ -202,48 +218,53 @@ func englishSet() TranslationSet {
|
|||
OpenInBrowser: "open in browser (first port is http)",
|
||||
SortContainersByState: "sort containers by state",
|
||||
|
||||
GlobalTitle: "Global",
|
||||
MainTitle: "Main",
|
||||
ProjectTitle: "Project",
|
||||
ServicesTitle: "Services",
|
||||
ContainersTitle: "Containers",
|
||||
StandaloneContainersTitle: "Standalone Containers",
|
||||
ImagesTitle: "Images",
|
||||
VolumesTitle: "Volumes",
|
||||
CustomCommandTitle: "Custom Command:",
|
||||
BulkCommandTitle: "Bulk Command:",
|
||||
ErrorTitle: "Error",
|
||||
LogsTitle: "Logs",
|
||||
ConfigTitle: "Config",
|
||||
EnvTitle: "Env",
|
||||
DockerComposeConfigTitle: "Docker-Compose Config",
|
||||
TopTitle: "Top",
|
||||
StatsTitle: "Stats",
|
||||
CreditsTitle: "About",
|
||||
ContainerConfigTitle: "Container Config",
|
||||
ContainerEnvTitle: "Container Env",
|
||||
NothingToDisplay: "Nothing to display",
|
||||
NoContainerForService: "No logs to show; service is not associated with a container",
|
||||
CannotDisplayEnvVariables: "Something went wrong while displaying environment variables",
|
||||
GlobalTitle: "Global",
|
||||
MainTitle: "Main",
|
||||
ProjectTitle: "Project",
|
||||
ServicesTitle: "Services",
|
||||
ContainersTitle: "Containers",
|
||||
StandaloneContainersTitle: "Standalone Containers",
|
||||
ImagesTitle: "Images",
|
||||
VolumesTitle: "Volumes",
|
||||
NetworksTitle: "Networks",
|
||||
CustomCommandTitle: "Custom Command:",
|
||||
BulkCommandTitle: "Bulk Command:",
|
||||
ErrorTitle: "Error",
|
||||
LogsTitle: "Logs",
|
||||
ConfigTitle: "Config",
|
||||
EnvTitle: "Env",
|
||||
DockerComposeConfigTitle: "Docker-Compose Config",
|
||||
TopTitle: "Top",
|
||||
StatsTitle: "Stats",
|
||||
CreditsTitle: "About",
|
||||
ContainerConfigTitle: "Container Config",
|
||||
ContainerEnvTitle: "Container Env",
|
||||
NothingToDisplay: "Nothing to display",
|
||||
NoContainerForService: "No logs to show; service is not associated with a container",
|
||||
CannotDisplayEnvVariables: "Something went wrong while displaying environment variables",
|
||||
CannotManageNonLocalService: "This service belongs to a different compose project. Run lazydocker from that project's directory to manage it.",
|
||||
|
||||
NoContainers: "No containers",
|
||||
NoContainer: "No container",
|
||||
NoImages: "No images",
|
||||
NoVolumes: "No volumes",
|
||||
NoNetworks: "No networks",
|
||||
NoServices: "No services",
|
||||
|
||||
ConfirmQuit: "Are you sure you want to quit?",
|
||||
ConfirmUpProject: "Are you sure you want to 'up' your docker compose project?",
|
||||
MustForceToRemoveContainer: "You cannot remove a running container unless you force it. Do you want to force it?",
|
||||
NotEnoughSpace: "Not enough space to render panels",
|
||||
ConfirmPruneImages: "Are you sure you want to prune all unused images?",
|
||||
ConfirmPruneContainers: "Are you sure you want to prune all stopped containers?",
|
||||
ConfirmStopContainers: "Are you sure you want to stop all containers?",
|
||||
ConfirmRemoveContainers: "Are you sure you want to remove all containers?",
|
||||
ConfirmPruneVolumes: "Are you sure you want to prune all unused volumes?",
|
||||
StopService: "Are you sure you want to stop this service's containers?",
|
||||
StopContainer: "Are you sure you want to stop this container?",
|
||||
PressEnterToReturn: "Press enter to return to lazydocker (this prompt can be disabled in your config by setting `gui.returnImmediately: true`)",
|
||||
ConfirmQuit: "Are you sure you want to quit?",
|
||||
ConfirmUpProject: "Are you sure you want to 'up' your docker compose project?",
|
||||
MustForceToRemoveContainer: "You cannot remove a running container unless you force it. Do you want to force it?",
|
||||
NotEnoughSpace: "Not enough space to render panels",
|
||||
ConfirmPruneImages: "Are you sure you want to prune all unused images?",
|
||||
ConfirmPruneContainers: "Are you sure you want to prune all stopped containers?",
|
||||
ConfirmStopContainers: "Are you sure you want to stop all containers?",
|
||||
ConfirmRemoveContainers: "Are you sure you want to remove all containers?",
|
||||
ConfirmPruneVolumes: "Are you sure you want to prune all unused volumes?",
|
||||
ConfirmPruneNetworks: "Are you sure you want to prune all unused networks?",
|
||||
StopService: "Are you sure you want to stop this service's containers?",
|
||||
StopContainer: "Are you sure you want to stop this container?",
|
||||
PressEnterToReturn: "Press enter to return to lazydocker (this prompt can be disabled in your config by setting `gui.returnImmediately: true`)",
|
||||
DetachFromContainerShortCut: "By default, to detach from the container press ctrl-p then ctrl-q",
|
||||
|
||||
No: "no",
|
||||
Yes: "yes",
|
||||
|
|
@ -251,5 +272,12 @@ func englishSet() TranslationSet {
|
|||
LcNextScreenMode: "next screen mode (normal/half/fullscreen)",
|
||||
LcPrevScreenMode: "prev screen mode",
|
||||
FilterPrompt: "filter",
|
||||
|
||||
FocusProjects: "focus projects panel",
|
||||
FocusServices: "focus services panel",
|
||||
FocusContainers: "focus containers panel",
|
||||
FocusImages: "focus images panel",
|
||||
FocusVolumes: "focus volumes panel",
|
||||
FocusNetworks: "focus networks panel",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,42 +3,42 @@ package i18n
|
|||
func frenchSet() TranslationSet {
|
||||
return TranslationSet{
|
||||
PruningStatus: "destruction",
|
||||
RemovingStatus: "supression",
|
||||
RemovingStatus: "suppression",
|
||||
RestartingStatus: "redémarrage",
|
||||
StartingStatus: "démarrage",
|
||||
StoppingStatus: "arrêt",
|
||||
PausingStatus: "mise en pause",
|
||||
RunningCustomCommandStatus: "execution de la commande personalisée",
|
||||
RunningBulkCommandStatus: "execution de la commande groupée",
|
||||
RunningCustomCommandStatus: "exécution de la commande personalisée",
|
||||
RunningBulkCommandStatus: "exécution de la commande groupée",
|
||||
|
||||
NoViewMachingNewLineFocusedSwitchStatement: "No view matching newLineFocused switch statement",
|
||||
NoViewMachingNewLineFocusedSwitchStatement: "Aucune vue correspondant au switch newLineFocused",
|
||||
|
||||
ErrorOccurred: "Une erreur s'est produite! Veuillez créer un rapport d'erreur sur https://github.com/jesseduffield/lazydocker/issues",
|
||||
ConnectionFailed: "Erreur lors de la connexion au client docker. Essayez de redémarrer votre client docker",
|
||||
UnattachableContainerError: "Le conteneur ne peut pas être attaché. Vous devez executer le service avec le drapeau 'it' ou bien utiliser `stdin_open: true, tty: true` dans votre fichier docker-compose.yaml",
|
||||
WaitingForContainerInfo: "Le processus ne peut pas continuer avant que docker donne plus d'informations. Veuillez réessayer dans quelques instants.",
|
||||
ErrorOccurred: "Une erreur s'est produite ! Veuillez créer un rapport d'erreur sur https://github.com/jesseduffield/lazydocker/issues",
|
||||
ConnectionFailed: "Erreur lors de la connexion au client Docker. Essayez de redémarrer votre client Docker",
|
||||
UnattachableContainerError: "Le conteneur ne peut pas être attaché. Vous devez exécuter le service avec le drapeau 'it' ou bien utiliser `stdin_open: true, tty: true` dans votre fichier docker-compose.yml",
|
||||
WaitingForContainerInfo: "Le processus ne peut pas continuer avant que Docker ne fournisse plus d'informations. Veuillez réessayer dans quelques instants.",
|
||||
|
||||
CannotAttachStoppedContainerError: "Vous ne pouvez pas vous attacher à un conteneur en arrêt, vous devez le démarrer avant (ce que vous pouvez faire avec la touche 'r') (oui, je suis trop paresseux pour le faire automatiquement pour vous) (plutôt cool que je puisse communiquer en tête-à-tête avec vous au travers d'un message d'erreur)",
|
||||
CannotAccessDockerSocketError: "Impossible d'accèder à la socket docker à: unix:///var/run/docker.sock\nLancez lazydocker en tant que root ou alors lisez https://docs.docker.com/install/linux/linux-postinstall/",
|
||||
CannotKillChildError: "Trois secondes ont étés attendu pour l'arrêt des processus fils. Il y a possiblement un processus orphelin qui continue à tourner sur votre systeme.",
|
||||
CannotAttachStoppedContainerError: "Vous ne pouvez pas vous attacher à un conteneur arrêté, vous devez le démarrer en amont (ce que vous pouvez faire avec la touche 'r') (oui, je suis trop paresseux pour le faire automatiquement pour vous) (plutôt cool que je puisse communiquer en tête-à-tête avec vous au travers d'un message d'erreur, cependant)",
|
||||
CannotAccessDockerSocketError: "Impossible d'accéder au socket Docker à : unix:///var/run/docker.sock\nLancez lazydocker en tant que root ou alors lisez https://docs.docker.com/install/linux/linux-postinstall/",
|
||||
CannotKillChildError: "Trois secondes se sont écoulées depuis la demande d'arrêt des processus enfants. Il se peut qu'un processus orphelin continue à tourner sur votre système.",
|
||||
|
||||
Donate: "Donner",
|
||||
Confirm: "Confirmer",
|
||||
|
||||
Return: "retour",
|
||||
FocusMain: "focus paneau principal",
|
||||
FocusMain: "focus panneau principal",
|
||||
Navigate: "naviguer",
|
||||
Execute: "executer",
|
||||
Execute: "exécuter",
|
||||
Close: "fermer",
|
||||
Menu: "menu",
|
||||
MenuTitle: "Menu",
|
||||
Scroll: "faire défiler",
|
||||
OpenConfig: "ouvrire la configuration lazydocker",
|
||||
OpenConfig: "ouvrir la configuration lazydocker",
|
||||
EditConfig: "modifier la configuration lazydocker",
|
||||
Cancel: "annuler",
|
||||
Remove: "supprimer",
|
||||
HideStopped: "cacher/montrer les conteneurs arrêtés",
|
||||
ForceRemove: "forcer la supression",
|
||||
ForceRemove: "forcer la suppression",
|
||||
RemoveWithVolumes: "supprimer avec les volumes",
|
||||
RemoveService: "supprimer les conteneurs",
|
||||
Stop: "arrêter",
|
||||
|
|
@ -53,19 +53,21 @@ func frenchSet() TranslationSet {
|
|||
ViewLogs: "voir les enregistrements",
|
||||
RemoveImage: "supprimer l'image",
|
||||
RemoveVolume: "supprimer le volume",
|
||||
RemoveWithoutPrune: "supprimer sans effacer les parents non étiqueté",
|
||||
RemoveWithoutPruneWithForce: "supprimer (forcer) sans effacer les parents non étiqueté",
|
||||
RemoveNetwork: "supprimer le réseau",
|
||||
RemoveWithoutPrune: "supprimer sans effacer les parents non étiquetés",
|
||||
RemoveWithoutPruneWithForce: "supprimer (forcer) sans effacer les parents non étiquetés",
|
||||
RemoveWithForce: "supprimer (forcer)",
|
||||
PruneContainers: "détruire les conteneurs arrêtes",
|
||||
PruneContainers: "détruire les conteneurs arrêtés",
|
||||
PruneVolumes: "détruire les volumes non utilisés",
|
||||
PruneImages: "détruire les images non utilisés",
|
||||
PruneNetworks: "détruire les réseaux non utilisés",
|
||||
PruneImages: "détruire les images non utilisées",
|
||||
StopAllContainers: "arrêter tous les conteneurs",
|
||||
RemoveAllContainers: "supprimer tous les conteneurs (forcer)",
|
||||
ViewRestartOptions: "voir les options de redémarrage",
|
||||
ExecShell: "executer le shell",
|
||||
RunCustomCommand: "executer une commande prédéfinie",
|
||||
ViewBulkCommands: "voir les commandes groupés",
|
||||
OpenInBrowser: "ouvrir dans le navgateur (le premier port est http)",
|
||||
ExecShell: "exécuter le shell",
|
||||
RunCustomCommand: "exécuter une commande prédéfinie",
|
||||
ViewBulkCommands: "voir les commandes groupées",
|
||||
OpenInBrowser: "ouvrir dans le navigateur (le premier port est http)",
|
||||
SortContainersByState: "ordonner les conteneurs par état",
|
||||
|
||||
GlobalTitle: "Global",
|
||||
|
|
@ -73,40 +75,44 @@ func frenchSet() TranslationSet {
|
|||
ProjectTitle: "Projet",
|
||||
ServicesTitle: "Services",
|
||||
ContainersTitle: "Conteneurs",
|
||||
StandaloneContainersTitle: "Conteneurs Autonomes",
|
||||
StandaloneContainersTitle: "Conteneurs autonomes",
|
||||
ImagesTitle: "Images",
|
||||
VolumesTitle: "Volumes",
|
||||
CustomCommandTitle: "Commande personalisé:",
|
||||
BulkCommandTitle: "Commande groupée:",
|
||||
NetworksTitle: "Réseaux",
|
||||
CustomCommandTitle: "Commande personnalisée :",
|
||||
BulkCommandTitle: "Commande groupée :",
|
||||
ErrorTitle: "Erreur",
|
||||
LogsTitle: "Enregistrements",
|
||||
LogsTitle: "Journaux",
|
||||
ConfigTitle: "Config",
|
||||
EnvTitle: "Env",
|
||||
DockerComposeConfigTitle: "Config Docker-Compose",
|
||||
TopTitle: "Top",
|
||||
StatsTitle: "Statistiques",
|
||||
CreditsTitle: "A propos",
|
||||
CreditsTitle: "À propos",
|
||||
ContainerConfigTitle: "Config Conteneur",
|
||||
ContainerEnvTitle: "Env Conteneur",
|
||||
NothingToDisplay: "Rien à afficher",
|
||||
CannotDisplayEnvVariables: "Quelque chose à échoué lors de l'affichage des variables d'environnement",
|
||||
CannotDisplayEnvVariables: "Quelque chose a échoué lors de l'affichage des variables d'environnement",
|
||||
|
||||
NoContainers: "Aucun conteneurs",
|
||||
NoContainers: "Aucun conteneur",
|
||||
NoContainer: "Aucun conteneur",
|
||||
NoImages: "Aucune images",
|
||||
NoVolumes: "Aucun volumes",
|
||||
NoImages: "Aucune image",
|
||||
NoVolumes: "Aucun volume",
|
||||
NoNetworks: "Aucun réseau",
|
||||
|
||||
ConfirmQuit: "Êtes vous certain de vouloir quitter?",
|
||||
MustForceToRemoveContainer: "Vous ne pouvez pas supprimer un conteneur qui tourne sans le forcer. Voulez-vous le forcer ?",
|
||||
NotEnoughSpace: "Manque d'espace pour afficher les differents panneaux",
|
||||
ConfirmPruneImages: "Êtes-vous certain de vouloir détruire toutes les images non utilisés ?",
|
||||
ConfirmPruneContainers: "Êtes-vous certain de vouloir détruire tous les conteneurs arrêtés ?",
|
||||
ConfirmStopContainers: "Êtes-vous certain de vouloir arrêter tous les conteneurs ?",
|
||||
ConfirmRemoveContainers: "Êtes-vous certain de vouloir supprimer tous les conteneurs ?",
|
||||
ConfirmPruneVolumes: "Êtes-vous certain de vouloir détruire tous les volumes non utilisés ?",
|
||||
StopService: "Êtes-vous certain de vouloir arrêter le conteneur de ce service ?",
|
||||
StopContainer: "Êtes-vous certain de vouloir arrêter ce conteneur ?",
|
||||
PressEnterToReturn: "Appuiez sur Enter pour revenir à lazydocker (ce message peut être désactivé dans vos configurations en appliquant `gui.returnImmediately: true`)",
|
||||
ConfirmQuit: "Êtes-vous certain de vouloir quitter ?",
|
||||
MustForceToRemoveContainer: "Vous ne pouvez pas supprimer un conteneur qui tourne sans le forcer. Voulez-vous le forcer ?",
|
||||
NotEnoughSpace: "Manque d'espace pour afficher les différent panneaux",
|
||||
ConfirmPruneImages: "Êtes-vous certain de vouloir détruire toutes les images non utilisées ?",
|
||||
ConfirmPruneContainers: "Êtes-vous certain de vouloir détruire tous les conteneurs arrêtés ?",
|
||||
ConfirmStopContainers: "Êtes-vous certain de vouloir arrêter tous les conteneurs ?",
|
||||
ConfirmRemoveContainers: "Êtes-vous certain de vouloir supprimer tous les conteneurs ?",
|
||||
ConfirmPruneVolumes: "Êtes-vous certain de vouloir détruire tous les volumes non utilisés ?",
|
||||
ConfirmPruneNetworks: "Êtes-vous certain de vouloir détruire tous les réseaux non utilisés ?",
|
||||
StopService: "Êtes-vous certain de vouloir arrêter le conteneur de ce service ?",
|
||||
StopContainer: "Êtes-vous certain de vouloir arrêter ce conteneur ?",
|
||||
PressEnterToReturn: "Appuyez sur Entrée pour revenir à lazydocker (ce message peut être désactivé dans vos configurations en appliquant `gui.returnImmediately: true`)",
|
||||
DetachFromContainerShortCut: "Par défaut, pour se détacher du conteneur appuyez sur CTRL-P puis CTRL-Q",
|
||||
|
||||
No: "non",
|
||||
Yes: "oui",
|
||||
|
|
|
|||
|
|
@ -44,9 +44,11 @@ func germanSet() TranslationSet {
|
|||
ViewLogs: "zeige Protokolle",
|
||||
RemoveImage: "entferne Image",
|
||||
RemoveVolume: "entferne Volume",
|
||||
RemoveNetwork: "entferne Netzwerk",
|
||||
RemoveWithoutPrune: "entfernen, ohne die unmarkierten Eltern zu entfernen",
|
||||
PruneContainers: "entferne verlassene Container",
|
||||
PruneVolumes: "entferne unbenutzte Volumes",
|
||||
PruneNetworks: "entferne unbenutzte Netzwerk",
|
||||
PruneImages: "entferne unbenutzte Images",
|
||||
ViewRestartOptions: "zeige Neustartoptionen",
|
||||
RunCustomCommand: "führe vordefinierten benutzerdefinierten Befehl aus",
|
||||
|
|
@ -59,6 +61,7 @@ func germanSet() TranslationSet {
|
|||
StandaloneContainersTitle: "Alleinstehende Container",
|
||||
ImagesTitle: "Images",
|
||||
VolumesTitle: "Volumes",
|
||||
NetworksTitle: "Netzwerk",
|
||||
CustomCommandTitle: "Benutzerdefinierter Befehl",
|
||||
ErrorTitle: "Fehler",
|
||||
LogsTitle: "Protokoll",
|
||||
|
|
@ -77,15 +80,18 @@ func germanSet() TranslationSet {
|
|||
NoContainer: "Kein Container",
|
||||
NoImages: "Keine Images",
|
||||
NoVolumes: "Keine Volumes",
|
||||
NoNetworks: "Keine Netzwerk",
|
||||
|
||||
ConfirmQuit: "Bist du dir sicher, dass du verlassen möchtest?",
|
||||
MustForceToRemoveContainer: "Du kannst keinen Container entfernen, der noch ausgeführt wird außer du erzwingst es. Möchtest du es erzwingen?",
|
||||
NotEnoughSpace: "Nicht genug Platz um die Panel darzustellen",
|
||||
ConfirmPruneImages: "Bist du dir sicher, dass du alle unbenutzten Images entfernen möchtest?",
|
||||
ConfirmPruneContainers: "Bist du dir sicher, dass du alle angehaltenen Container entfernen möchtes?",
|
||||
ConfirmPruneVolumes: "Bist du dir sicher, dass du alle unbenutzen Volumes entfernen möchtest?",
|
||||
StopService: "Bist du dir sicher, dass du den Dienst dieses Containers anhalten möchtest?",
|
||||
StopContainer: "Bist du dir sicher, dass du den Container anhalten möchtest?",
|
||||
PressEnterToReturn: "Drücke Eingabe um zu lazydocker zurückzukehren. (Diese Nachfrage kann in Deiner Konfiguration deaktiviert werden, indem du folgenden Wert setzt: `gui.returnImmediately: true`)",
|
||||
ConfirmQuit: "Bist du dir sicher, dass du verlassen möchtest?",
|
||||
MustForceToRemoveContainer: "Du kannst keinen Container entfernen, der noch ausgeführt wird außer du erzwingst es. Möchtest du es erzwingen?",
|
||||
NotEnoughSpace: "Nicht genug Platz um die Panel darzustellen",
|
||||
ConfirmPruneImages: "Bist du dir sicher, dass du alle unbenutzten Images entfernen möchtest?",
|
||||
ConfirmPruneContainers: "Bist du dir sicher, dass du alle angehaltenen Container entfernen möchtes?",
|
||||
ConfirmPruneVolumes: "Bist du dir sicher, dass du alle unbenutzen Volumes entfernen möchtest?",
|
||||
ConfirmPruneNetworks: "Bist du dir sicher, dass du alle unbenutzen Netzwerk entfernen möchtest?",
|
||||
StopService: "Bist du dir sicher, dass du den Dienst dieses Containers anhalten möchtest?",
|
||||
StopContainer: "Bist du dir sicher, dass du den Container anhalten möchtest?",
|
||||
PressEnterToReturn: "Drücke Eingabe um zu lazydocker zurückzukehren. (Diese Nachfrage kann in Deiner Konfiguration deaktiviert werden, indem du folgenden Wert setzt: `gui.returnImmediately: true`)",
|
||||
DetachFromContainerShortCut: "Um sich vom Container zu trennen, drücken Sie standardmäßig ctrl-p und dann ctrl-q",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,9 @@ func GetTranslationSets() map[string]TranslationSet {
|
|||
"tr": turkishSet(),
|
||||
"en": englishSet(),
|
||||
"fr": frenchSet(),
|
||||
"zh": chineseSet(),
|
||||
"es": spanishSet(),
|
||||
"pt": portugueseSet(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,9 +44,11 @@ func polishSet() TranslationSet {
|
|||
ViewLogs: "pokaż logi",
|
||||
RemoveImage: "usuń obraz",
|
||||
RemoveVolume: "usuń wolumen",
|
||||
RemoveNetwork: "usuń sieci",
|
||||
RemoveWithoutPrune: "usuń bez kasowania nieoznaczonych rodziców",
|
||||
PruneContainers: "wyczyść kontenery",
|
||||
PruneVolumes: "wyczyść nieużywane wolumeny",
|
||||
PruneNetworks: "wyczyść nieużywane sieci",
|
||||
PruneImages: "wyczyść nieużywane obrazy",
|
||||
ViewRestartOptions: "pokaż opcje restartu",
|
||||
RunCustomCommand: "wykonaj predefiniowaną własną komende",
|
||||
|
|
@ -59,6 +61,7 @@ func polishSet() TranslationSet {
|
|||
StandaloneContainersTitle: "Kontenery samodzielne",
|
||||
ImagesTitle: "Obrazy",
|
||||
VolumesTitle: "Wolumeny",
|
||||
NetworksTitle: "Sieci",
|
||||
CustomCommandTitle: "Własna komenda:",
|
||||
ErrorTitle: "Błąd",
|
||||
LogsTitle: "Logi",
|
||||
|
|
@ -77,15 +80,18 @@ func polishSet() TranslationSet {
|
|||
NoContainer: "Brak kontenera",
|
||||
NoImages: "Brak obrazów",
|
||||
NoVolumes: "Brak wolumenów",
|
||||
NoNetworks: "Brak sieci",
|
||||
|
||||
ConfirmQuit: "Na pewno chcesz wyjść?",
|
||||
MustForceToRemoveContainer: "Nie możesz usunąć uruchomionego kontenera dopóki nie zrobisz tego siłą. Chcesz wykonać to z siłą?",
|
||||
NotEnoughSpace: "Niedostateczna ilość miejsca do wyświetlenia paneli",
|
||||
ConfirmPruneImages: "Na pewno wyczyścić wszystkie nieużywane obrazy?",
|
||||
ConfirmPruneContainers: "Na pewno wyczyścić wszystkie nieuruchomione kontenery?",
|
||||
ConfirmPruneVolumes: "Na pewno wyczyścić wszystkie nieużywane wolumeny?",
|
||||
StopService: "Na pewno zatrzymać kontenery tego serwisu?",
|
||||
StopContainer: "Na pewno zatrzymać ten kontener?",
|
||||
PressEnterToReturn: "Wciśnij enter aby powrócić do lazydockera (ten komunikat może być wyłączony w konfiguracji poprzez ustawienie `gui.returnImmediately: true`)",
|
||||
ConfirmQuit: "Na pewno chcesz wyjść?",
|
||||
MustForceToRemoveContainer: "Nie możesz usunąć uruchomionego kontenera dopóki nie zrobisz tego siłą. Chcesz wykonać to z siłą?",
|
||||
NotEnoughSpace: "Niedostateczna ilość miejsca do wyświetlenia paneli",
|
||||
ConfirmPruneImages: "Na pewno wyczyścić wszystkie nieużywane obrazy?",
|
||||
ConfirmPruneContainers: "Na pewno wyczyścić wszystkie nieuruchomione kontenery?",
|
||||
ConfirmPruneVolumes: "Na pewno wyczyścić wszystkie nieużywane wolumeny?",
|
||||
ConfirmPruneNetworks: "Na pewno wyczyścić wszystkie nieużywane sieci?",
|
||||
StopService: "Na pewno zatrzymać kontenery tego serwisu?",
|
||||
StopContainer: "Na pewno zatrzymać ten kontener?",
|
||||
PressEnterToReturn: "Wciśnij enter aby powrócić do lazydockera (ten komunikat może być wyłączony w konfiguracji poprzez ustawienie `gui.returnImmediately: true`)",
|
||||
DetachFromContainerShortCut: "Domyślnie, aby odłączyć się od kontenera, naciśnij ctrl-p, a następnie ctrl-q",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
137
pkg/i18n/portuguese.go
Normal file
137
pkg/i18n/portuguese.go
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
package i18n
|
||||
|
||||
func portugueseSet() TranslationSet {
|
||||
return TranslationSet{
|
||||
PruningStatus: "destruindo",
|
||||
RemovingStatus: "removendo",
|
||||
RestartingStatus: "reiniciando",
|
||||
StartingStatus: "iniciando",
|
||||
StoppingStatus: "parando",
|
||||
UppingServiceStatus: "subindo serviço",
|
||||
UppingProjectStatus: "subindo projeto",
|
||||
DowningStatus: "derrubando",
|
||||
PausingStatus: "pausando",
|
||||
RunningCustomCommandStatus: "executando comando personalizado",
|
||||
RunningBulkCommandStatus: "executando comando em massa",
|
||||
|
||||
NoViewMachingNewLineFocusedSwitchStatement: "No view matching newLineFocused switch statement",
|
||||
|
||||
ErrorOccurred: "Um erro ocorreu! Por favor, crie uma issue em https://github.com/jesseduffield/lazydocker/issues",
|
||||
ConnectionFailed: "Falha na conexão com o cliente Docker. Você pode precisar reiniciar o seu cliente Docker",
|
||||
UnattachableContainerError: "O contêiner não suporta anexação. Você deve executar o serviço com a flag '-it' ou usar `stdin_open: true, tty: true` no arquivo docker-compose.yml",
|
||||
WaitingForContainerInfo: "Não é possível prosseguir até que o Docker forneça mais informações sobre o contêiner. Por favor, tente novamente em alguns momentos.",
|
||||
CannotAttachStoppedContainerError: "Você não pode anexar a um contêiner parado, você precisa iniciá-lo primeiro (o que você pode fazer com a tecla 'r') (sim, sou preguiçoso demais para fazer isso automaticamente para você) (aliás, bem legal que eu posso me comunicar diretamente com você na forma de uma mensagem de erro)",
|
||||
CannotAccessDockerSocketError: "Não é possível acessar o sôquete docker em: unix:///var/run/docker.sock\nExecute o lazydocker como root ou leia https://docs.docker.com/install/linux/linux-postinstall/",
|
||||
CannotKillChildError: "Três segundos foram esperarados para que os processos filhos parassem. Pode haver um processo órfão que continua em execução em seu sistema.",
|
||||
|
||||
Donate: "Doar",
|
||||
Confirm: "Confirmar",
|
||||
|
||||
Return: "retornar",
|
||||
FocusMain: "focar no painel principal",
|
||||
LcFilter: "filtrar lista",
|
||||
Navigate: "navegar",
|
||||
Execute: "executar",
|
||||
Close: "fechar",
|
||||
Quit: "sair",
|
||||
Menu: "menu",
|
||||
MenuTitle: "Menu",
|
||||
Scroll: "rolar",
|
||||
OpenConfig: "abrir configuração do lazydocker",
|
||||
EditConfig: "editar configuração do lazydocker",
|
||||
Cancel: "cancelar",
|
||||
Remove: "remover",
|
||||
HideStopped: "ocultar/mostrar contêineres parados",
|
||||
ForceRemove: "forçar remoção",
|
||||
RemoveWithVolumes: "remover com volumes",
|
||||
RemoveService: "remover contêineres",
|
||||
UpService: "subir serviço",
|
||||
Stop: "parar",
|
||||
Pause: "pausar",
|
||||
Restart: "reiniciar",
|
||||
Down: "derrubar projeto",
|
||||
DownWithVolumes: "derrubar projetos com volumes",
|
||||
Start: "iniciar",
|
||||
Rebuild: "reconstuir",
|
||||
Recreate: "recriar",
|
||||
PreviousContext: "aba anterior",
|
||||
NextContext: "próxima aba",
|
||||
Attach: "anexar",
|
||||
ViewLogs: "ver logs",
|
||||
UpProject: "subir projeto",
|
||||
DownProject: "derrubar projeto",
|
||||
RemoveImage: "remover imagem",
|
||||
RemoveVolume: "remover volume",
|
||||
RemoveNetwork: "remover rede",
|
||||
RemoveWithoutPrune: "remover sem deletar pais não etiquetados",
|
||||
RemoveWithoutPruneWithForce: "remover (forçado) sem deletar pais não etiquetados",
|
||||
RemoveWithForce: "remover (forçado)",
|
||||
PruneContainers: "destruir contêineres encerrados",
|
||||
PruneVolumes: "destruir volumes não utilizados",
|
||||
PruneNetworks: "destruir redes não utilizadas",
|
||||
PruneImages: "destruir imagens não utilizadas",
|
||||
StopAllContainers: "parar todos os contêineres",
|
||||
RemoveAllContainers: "remover todos os contêineres (forçado)",
|
||||
ViewRestartOptions: "ver opções de reinício",
|
||||
ExecShell: "executar shell",
|
||||
RunCustomCommand: "executar comando personalizado predefinido",
|
||||
ViewBulkCommands: "ver comandos em massa",
|
||||
FilterList: "filtrar lista",
|
||||
OpenInBrowser: "abrir no navegador (primeira porta é http)",
|
||||
SortContainersByState: "ordenar contêineres por estado",
|
||||
|
||||
GlobalTitle: "Global",
|
||||
MainTitle: "Principal",
|
||||
ProjectTitle: "Projeto",
|
||||
ServicesTitle: "Serviços",
|
||||
ContainersTitle: "Contêineres",
|
||||
StandaloneContainersTitle: "Contêineres Avulsos",
|
||||
ImagesTitle: "Imagens",
|
||||
VolumesTitle: "Volumes",
|
||||
NetworksTitle: "Redes",
|
||||
CustomCommandTitle: "Comando Personalizado:",
|
||||
BulkCommandTitle: "Comando em Massa:",
|
||||
ErrorTitle: "Erro",
|
||||
LogsTitle: "Registros",
|
||||
ConfigTitle: "Config",
|
||||
EnvTitle: "Env",
|
||||
DockerComposeConfigTitle: "Docker-Compose Config",
|
||||
TopTitle: "Topo",
|
||||
StatsTitle: "Estatísticas",
|
||||
CreditsTitle: "Sobre",
|
||||
ContainerConfigTitle: "Configuração do Contêiner",
|
||||
ContainerEnvTitle: "Contêiner Env",
|
||||
NothingToDisplay: "Nada a exibir",
|
||||
NoContainerForService: "Nenhum log para exibir; o serviço não está associado a nenhum contêiner",
|
||||
CannotDisplayEnvVariables: "Algo deu errado ao exibir as variáveis de ambiente",
|
||||
|
||||
NoContainers: "Sem contêineres",
|
||||
NoContainer: "Sem contêiner",
|
||||
NoImages: "Sem imagens",
|
||||
NoVolumes: "Sem volumes",
|
||||
NoNetworks: "Sem redes",
|
||||
NoServices: "Sem serviços",
|
||||
|
||||
ConfirmQuit: "Tem certeza que deseja sair?",
|
||||
ConfirmUpProject: "Tem certeza que deseja 'iniciar' seu projeto docker compose?",
|
||||
MustForceToRemoveContainer: "Você não pode remover um contêiner em execução a menos que o force. Deseja forçar?",
|
||||
NotEnoughSpace: "Sem espaço suficiente para renderizar os painéis",
|
||||
ConfirmPruneImages: "Tem certeza que deseja eliminar todas as imagens não utilizadas?",
|
||||
ConfirmPruneContainers: "Tem certeza que deseja destruir todos os contêineres parados?",
|
||||
ConfirmStopContainers: "Tem certeza que deseja parar todos os contêineres?",
|
||||
ConfirmRemoveContainers: "Tem certeza que deseja remover todos os contêineres?",
|
||||
ConfirmPruneVolumes: "Tem certeza que deseja destruir todos os volumes não utilizados?",
|
||||
ConfirmPruneNetworks: "Tem certeza que deseja destruir todas as redes não utilizadas?",
|
||||
StopService: "Tem certeza que deseja parar os contêineres deste serviço?",
|
||||
StopContainer: "Tem certeza que deseja parar este contêiner?",
|
||||
PressEnterToReturn: "Pressione enter para retornar ao lazydocker (este prompt pode ser desativado em sua configuração definindo `gui.returnImmediately: true`)",
|
||||
DetachFromContainerShortCut: "Por padrão, para desanexar do contêiner, pressione ctrl-p e depois ctrl-q",
|
||||
|
||||
No: "não",
|
||||
Yes: "sim",
|
||||
|
||||
LcNextScreenMode: "modo de tela seguinte (normal/meia/tela cheia)",
|
||||
LcPrevScreenMode: "modo de tela anterior",
|
||||
FilterPrompt: "filtro",
|
||||
}
|
||||
}
|
||||
129
pkg/i18n/spanish.go
Normal file
129
pkg/i18n/spanish.go
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
package i18n
|
||||
|
||||
func spanishSet() TranslationSet {
|
||||
return TranslationSet{
|
||||
PruningStatus: "limpiando",
|
||||
RemovingStatus: "eliminando",
|
||||
RestartingStatus: "reiniciando",
|
||||
StartingStatus: "iniciando",
|
||||
StoppingStatus: "terminando",
|
||||
UppingServiceStatus: "levantando servicio",
|
||||
UppingProjectStatus: "levantando proyecto",
|
||||
DowningStatus: "dando de baja",
|
||||
PausingStatus: "pausando",
|
||||
RunningCustomCommandStatus: "ejecutando comando personalizado",
|
||||
RunningBulkCommandStatus: "ejecutando comando masivo",
|
||||
|
||||
ErrorOccurred: "¡Hubo un error! Por favor crea un issue en https://github.com/jesseduffield/lazydocker/issues",
|
||||
ConnectionFailed: "Falló la conexión con el docker client. Quizá necesitas reiniciar tu docker client",
|
||||
UnattachableContainerError: "Container does not support attaching. You must either run the service with the '-it' flag or use `stdin_open: true, tty: true` in the docker-compose.yml file",
|
||||
WaitingForContainerInfo: "No podemos proceder hasta que docker nos de más información sobre el contenedor. Inténtalo otra vez en unos segundos.",
|
||||
CannotAccessDockerSocketError: "No es posible acceder al docker socket en: unix:///var/run/docker.sock\nEjecuta lazydocker como root o lee https://docs.docker.com/install/linux/linux-postinstall/",
|
||||
CannotKillChildError: "Esperamos tres segundos a que el proceso hijo se detenga. Debe de haber un proceso huérfano que continua activo en tu sistema.",
|
||||
|
||||
Donate: "Donar",
|
||||
Confirm: "Confirmar",
|
||||
|
||||
Return: "regresar",
|
||||
FocusMain: "enfocar panel principal",
|
||||
LcFilter: "filtrar lista",
|
||||
Navigate: "navegar",
|
||||
Execute: "ejecutar",
|
||||
Close: "cerrar",
|
||||
Quit: "salir",
|
||||
Menu: "menú",
|
||||
MenuTitle: "Menú",
|
||||
OpenConfig: "abrir configuración de lazydocker",
|
||||
EditConfig: "editar configuración de lazydocker",
|
||||
Cancel: "cancelar",
|
||||
Remove: "borrar",
|
||||
HideStopped: "esconder/mostrar contenedores parados",
|
||||
ForceRemove: "borrar(forzado)",
|
||||
RemoveWithVolumes: "borrar con volúmenes",
|
||||
RemoveService: "borrar contenedores",
|
||||
UpService: "levantar servicio",
|
||||
Stop: "parar",
|
||||
Pause: "pausa",
|
||||
Restart: "reiniciar",
|
||||
Down: "bajar proyecto",
|
||||
DownWithVolumes: "bajar proyecto con volúmenes",
|
||||
Start: "iniciar",
|
||||
Rebuild: "recompilar",
|
||||
Recreate: "recrear",
|
||||
PreviousContext: "anterior pestaña",
|
||||
NextContext: "siguiente pestaña",
|
||||
ViewLogs: "ver logs",
|
||||
UpProject: "levantar proyecto",
|
||||
DownProject: "dar de baja el proyecto",
|
||||
RemoveImage: "limpiar imagen",
|
||||
RemoveVolume: "limpiar volúmen",
|
||||
RemoveNetwork: "limpiar red",
|
||||
RemoveWithoutPrune: "limpiar sin borrar padres sin etiqueta",
|
||||
RemoveWithoutPruneWithForce: "limpiar (forzado) sin borrar padres sin etiqueta",
|
||||
RemoveWithForce: "limpiar (forzado)",
|
||||
PruneContainers: "limpiar contenedores finalizados",
|
||||
PruneVolumes: "limpiar volúmenes sin usar",
|
||||
PruneNetworks: "limpiar redes sin usar",
|
||||
PruneImages: "limpiar imágenes sin usar",
|
||||
StopAllContainers: "detener todos los contenedores",
|
||||
RemoveAllContainers: "borrar todos los contenedores (forzado)",
|
||||
ViewRestartOptions: "ver opciones de reinicio",
|
||||
ExecShell: "ejecutar shell",
|
||||
RunCustomCommand: "ejecutar comando personalizado",
|
||||
ViewBulkCommands: "ver comandos masivos",
|
||||
FilterList: "filtar list",
|
||||
OpenInBrowser: "abrir en navegador (first port is http)",
|
||||
SortContainersByState: "ordenar contenedores por estado",
|
||||
|
||||
GlobalTitle: "Global",
|
||||
MainTitle: "Inicio",
|
||||
ProjectTitle: "Proyecto",
|
||||
ServicesTitle: "Servicios",
|
||||
ContainersTitle: "Contenedores",
|
||||
StandaloneContainersTitle: "Contenedores independientes",
|
||||
ImagesTitle: "Imágenes",
|
||||
VolumesTitle: "Volúmenes",
|
||||
NetworksTitle: "Redes",
|
||||
CustomCommandTitle: "Comando personalizado:",
|
||||
BulkCommandTitle: "Comando masivo:",
|
||||
ErrorTitle: "Error",
|
||||
LogsTitle: "Logs",
|
||||
ConfigTitle: "Configuración",
|
||||
EnvTitle: "Variables de entorno",
|
||||
DockerComposeConfigTitle: "Docker-Compose Config",
|
||||
TopTitle: "Top",
|
||||
StatsTitle: "Estadísticas",
|
||||
CreditsTitle: "Acerca",
|
||||
ContainerConfigTitle: "Configuración",
|
||||
ContainerEnvTitle: "Variables de entorno",
|
||||
NothingToDisplay: "Nada que mostrar",
|
||||
NoContainerForService: "No hay logs que mostrar; el servicio no está asociado con un contenedor",
|
||||
CannotDisplayEnvVariables: "Algo salió mal mientras se mostraban las variables de entorno",
|
||||
|
||||
NoContainers: "Sin contenedores",
|
||||
NoContainer: "Sin contenedor",
|
||||
NoImages: "Sin imágenes",
|
||||
NoVolumes: "Sin volúmenes",
|
||||
NoNetworks: "Sin redes",
|
||||
NoServices: "Sin servicios",
|
||||
|
||||
ConfirmQuit: "¿Realmente quieres salir?",
|
||||
ConfirmUpProject: "¿Realmente quieres levantar tu proyecto docker compose?",
|
||||
MustForceToRemoveContainer: "No puedes borrar un contenedor en ejecución a menos de que lo fuerces, ¿quieres hacerlo?",
|
||||
NotEnoughSpace: "No hay suficiente espacio para renderizar los paneles",
|
||||
ConfirmPruneImages: "¿Realmente quieres limpiar todas tus imágenes?",
|
||||
ConfirmPruneContainers: "¿Realmente quieres limpiar todos los contenedores finalizados?",
|
||||
ConfirmStopContainers: "¿Realmente quieres detener todos los contenedores?",
|
||||
ConfirmRemoveContainers: "¿Realmente quieres borrar todos los contenedores?",
|
||||
ConfirmPruneVolumes: "¿Realmente quieres limpiar todos los vólumenes sin usar?",
|
||||
ConfirmPruneNetworks: "¿Realmente quieres limpiar todas las redes sin usar?",
|
||||
StopService: "¿Realmente quieres detener los contenedores de este servicio?",
|
||||
StopContainer: "¿Realmente quieres detener este contenedor?",
|
||||
PressEnterToReturn: "Presionar [enter] para volver a lazydocker (este mensaje puede ser desactivado en tu configuración poniendo `gui.returnImmediately: true`)",
|
||||
|
||||
No: "no",
|
||||
Yes: "sí",
|
||||
|
||||
FilterPrompt: "filtrar",
|
||||
}
|
||||
}
|
||||
|
|
@ -44,9 +44,11 @@ func turkishSet() TranslationSet {
|
|||
ViewLogs: "kayıt defterini görüntüle",
|
||||
RemoveImage: "imajı kaldır",
|
||||
RemoveVolume: "alanı kaldır",
|
||||
RemoveNetwork: "ağı kaldır",
|
||||
RemoveWithoutPrune: "etkisiz ebeveynleri silmeden kaldır",
|
||||
PruneContainers: "çalışmayan konteynerleri temizle",
|
||||
PruneVolumes: "kullanılmayan alanları temizle",
|
||||
PruneNetworks: "kullanılmayan ağları temizle",
|
||||
PruneImages: "kullanılmayan imajları temizle",
|
||||
ViewRestartOptions: "yeniden başlatma seçeneklerini görüntüle",
|
||||
RunCustomCommand: "önceden tanımlanmış özel komutu çalıştır",
|
||||
|
|
@ -59,6 +61,7 @@ func turkishSet() TranslationSet {
|
|||
StandaloneContainersTitle: "Bağımsız Konteynerler",
|
||||
ImagesTitle: "Imajlar",
|
||||
VolumesTitle: "Alanlar",
|
||||
NetworksTitle: "Ağları",
|
||||
CustomCommandTitle: "Özel Komut:",
|
||||
ErrorTitle: "Hata",
|
||||
LogsTitle: "Kayitlar",
|
||||
|
|
@ -77,15 +80,18 @@ func turkishSet() TranslationSet {
|
|||
NoContainer: "Konteyner yok",
|
||||
NoImages: "Imajlar yok",
|
||||
NoVolumes: "Alanlar yok",
|
||||
NoNetworks: "Ağları yok",
|
||||
|
||||
ConfirmQuit: "Çıkmak istediğine emin misin?",
|
||||
MustForceToRemoveContainer: "Zorlamadan çalışan bir konteyneri kaldıramazsınız. Zorlamak ister misin?",
|
||||
NotEnoughSpace: "Panelleri oluşturmak için yeterli alan yok",
|
||||
ConfirmPruneImages: "Kullanılmayan tüm görüntüleri temizlemek istediğinize emin misiniz?",
|
||||
ConfirmPruneContainers: "Durdurulan tüm konteynerları temizlemek istediğinizden emin misiniz?",
|
||||
ConfirmPruneVolumes: "Kullanılmayan tüm alanları temizlemek istediğinizden emin misiniz?",
|
||||
StopService: "Bu servisin konteynerlerini durdurmak istediğinize emin misiniz?",
|
||||
StopContainer: "Bu konteyneri durdurmak istediğinize emin misiniz?",
|
||||
PressEnterToReturn: "lazydocker' a geri dönmek için enter tuşuna basın ( Bu uyarı, `gui.return Immediately: true` ayarıyla devre dışı bırakılabilir)",
|
||||
ConfirmQuit: "Çıkmak istediğine emin misin?",
|
||||
MustForceToRemoveContainer: "Zorlamadan çalışan bir konteyneri kaldıramazsınız. Zorlamak ister misin?",
|
||||
NotEnoughSpace: "Panelleri oluşturmak için yeterli alan yok",
|
||||
ConfirmPruneImages: "Kullanılmayan tüm görüntüleri temizlemek istediğinize emin misiniz?",
|
||||
ConfirmPruneContainers: "Durdurulan tüm konteynerları temizlemek istediğinizden emin misiniz?",
|
||||
ConfirmPruneVolumes: "Kullanılmayan tüm alanları temizlemek istediğinizden emin misiniz?",
|
||||
ConfirmPruneNetworks: "Kullanılmayan tüm ağları temizlemek istediğinizden emin misiniz?",
|
||||
StopService: "Bu servisin konteynerlerini durdurmak istediğinize emin misiniz?",
|
||||
StopContainer: "Bu konteyneri durdurmak istediğinize emin misiniz?",
|
||||
PressEnterToReturn: "lazydocker' a geri dönmek için enter tuşuna basın ( Bu uyarı, `gui.return Immediately: true` ayarıyla devre dışı bırakılabilir)",
|
||||
DetachFromContainerShortCut: "Varsayılan olarak, kaptan ayırmak için ctrl-p ve ardından ctrl-q tuşlarına basın",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package log
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ func newDevelopmentLogger(config *config.AppConfig) *logrus.Logger {
|
|||
|
||||
func newProductionLogger() *logrus.Logger {
|
||||
log := logrus.New()
|
||||
log.Out = ioutil.Discard
|
||||
log.Out = io.Discard
|
||||
log.SetLevel(logrus.ErrorLevel)
|
||||
return log
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,9 +65,12 @@ func (t *TaskManager) NewTask(f func(ctx context.Context)) error {
|
|||
|
||||
t.waitingMutex.Lock()
|
||||
defer t.waitingMutex.Unlock()
|
||||
t.taskIDMutex.Lock()
|
||||
if taskID < t.newTaskId {
|
||||
t.taskIDMutex.Unlock()
|
||||
return
|
||||
}
|
||||
t.taskIDMutex.Unlock()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
notifyStopped := make(chan struct{})
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package utils
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
|
|
@ -13,8 +14,14 @@ import (
|
|||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/mattn/go-runewidth"
|
||||
|
||||
// "github.com/jesseduffield/yaml"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/goccy/go-yaml/lexer"
|
||||
"github.com/goccy/go-yaml/printer"
|
||||
)
|
||||
|
||||
// SplitLines takes a multiline string and splits it on newlines
|
||||
|
|
@ -35,10 +42,10 @@ func SplitLines(multilineString string) []string {
|
|||
// WithPadding pads a string as much as you want
|
||||
func WithPadding(str string, padding int) string {
|
||||
uncoloredStr := Decolorise(str)
|
||||
if padding < len(uncoloredStr) {
|
||||
if padding < runewidth.StringWidth(uncoloredStr) {
|
||||
return str
|
||||
}
|
||||
return str + strings.Repeat(" ", padding-len(uncoloredStr))
|
||||
return str + strings.Repeat(" ", padding-runewidth.StringWidth(uncoloredStr))
|
||||
}
|
||||
|
||||
// ColoredString takes a string and a colour attribute and returns a colored
|
||||
|
|
@ -52,6 +59,45 @@ func ColoredString(str string, colorAttribute color.Attribute) string {
|
|||
return ColoredStringDirect(str, colour)
|
||||
}
|
||||
|
||||
// ColoredYamlString takes an YAML formatted string and returns a colored string
|
||||
// with colors hardcoded as:
|
||||
// keys: cyan
|
||||
// Booleans: magenta
|
||||
// Numbers: yellow
|
||||
// Strings: green
|
||||
func ColoredYamlString(str string) string {
|
||||
format := func(attr color.Attribute) string {
|
||||
return fmt.Sprintf("%s[%dm", "\x1b", attr)
|
||||
}
|
||||
tokens := lexer.Tokenize(str)
|
||||
var p printer.Printer
|
||||
p.Bool = func() *printer.Property {
|
||||
return &printer.Property{
|
||||
Prefix: format(color.FgMagenta),
|
||||
Suffix: format(color.Reset),
|
||||
}
|
||||
}
|
||||
p.Number = func() *printer.Property {
|
||||
return &printer.Property{
|
||||
Prefix: format(color.FgYellow),
|
||||
Suffix: format(color.Reset),
|
||||
}
|
||||
}
|
||||
p.MapKey = func() *printer.Property {
|
||||
return &printer.Property{
|
||||
Prefix: format(color.FgCyan),
|
||||
Suffix: format(color.Reset),
|
||||
}
|
||||
}
|
||||
p.String = func() *printer.Property {
|
||||
return &printer.Property{
|
||||
Prefix: format(color.FgGreen),
|
||||
Suffix: format(color.Reset),
|
||||
}
|
||||
}
|
||||
return p.PrintTokens(tokens)
|
||||
}
|
||||
|
||||
// MultiColoredString takes a string and an array of colour attributes and returns a colored
|
||||
// string with those attributes
|
||||
func MultiColoredString(str string, colorAttribute ...color.Attribute) string {
|
||||
|
|
@ -98,54 +144,52 @@ func Max(x, y int) int {
|
|||
}
|
||||
|
||||
// RenderTable takes an array of string arrays and returns a table containing the values
|
||||
func RenderTable(stringArrays [][]string) (string, error) {
|
||||
if len(stringArrays) == 0 {
|
||||
func RenderTable(rows [][]string) (string, error) {
|
||||
if len(rows) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
if !displayArraysAligned(stringArrays) {
|
||||
if !displayArraysAligned(rows) {
|
||||
return "", errors.New("Each item must return the same number of strings to display")
|
||||
}
|
||||
|
||||
padWidths := getPadWidths(stringArrays)
|
||||
paddedDisplayStrings := getPaddedDisplayStrings(stringArrays, padWidths)
|
||||
columnPadWidths := getPadWidths(rows)
|
||||
paddedDisplayRows := getPaddedDisplayStrings(rows, columnPadWidths)
|
||||
|
||||
return strings.Join(paddedDisplayStrings, "\n"), nil
|
||||
return strings.Join(paddedDisplayRows, "\n"), nil
|
||||
}
|
||||
|
||||
// Decolorise strips a string of color
|
||||
func Decolorise(str string) string {
|
||||
re := regexp.MustCompile(`\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]`)
|
||||
re := regexp.MustCompile(`\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mK]`)
|
||||
return re.ReplaceAllString(str, "")
|
||||
}
|
||||
|
||||
func getPadWidths(stringArrays [][]string) []int {
|
||||
if len(stringArrays[0]) <= 1 {
|
||||
func getPadWidths(rows [][]string) []int {
|
||||
if len(rows[0]) <= 1 {
|
||||
return []int{}
|
||||
}
|
||||
padWidths := make([]int, len(stringArrays[0])-1)
|
||||
for i := range padWidths {
|
||||
for _, strings := range stringArrays {
|
||||
uncoloredString := Decolorise(strings[i])
|
||||
if len(uncoloredString) > padWidths[i] {
|
||||
padWidths[i] = len(uncoloredString)
|
||||
columnPadWidths := make([]int, len(rows[0])-1)
|
||||
for i := range columnPadWidths {
|
||||
for _, cells := range rows {
|
||||
uncoloredCell := Decolorise(cells[i])
|
||||
|
||||
if runewidth.StringWidth(uncoloredCell) > columnPadWidths[i] {
|
||||
columnPadWidths[i] = runewidth.StringWidth(uncoloredCell)
|
||||
}
|
||||
}
|
||||
}
|
||||
return padWidths
|
||||
return columnPadWidths
|
||||
}
|
||||
|
||||
func getPaddedDisplayStrings(stringArrays [][]string, padWidths []int) []string {
|
||||
paddedDisplayStrings := make([]string, len(stringArrays))
|
||||
for i, stringArray := range stringArrays {
|
||||
if len(stringArray) == 0 {
|
||||
continue
|
||||
func getPaddedDisplayStrings(rows [][]string, columnPadWidths []int) []string {
|
||||
paddedDisplayRows := make([]string, len(rows))
|
||||
for i, cells := range rows {
|
||||
for j, columnPadWidth := range columnPadWidths {
|
||||
paddedDisplayRows[i] += WithPadding(cells[j], columnPadWidth) + " "
|
||||
}
|
||||
for j, padWidth := range padWidths {
|
||||
paddedDisplayStrings[i] += WithPadding(stringArray[j], padWidth) + " "
|
||||
}
|
||||
paddedDisplayStrings[i] += stringArray[len(padWidths)]
|
||||
paddedDisplayRows[i] += cells[len(columnPadWidths)]
|
||||
}
|
||||
return paddedDisplayStrings
|
||||
return paddedDisplayRows
|
||||
}
|
||||
|
||||
// displayArraysAligned returns true if every string array returned from our
|
||||
|
|
@ -339,3 +383,30 @@ func IsValidHexValue(v string) bool {
|
|||
func OpensMenuStyle(str string) string {
|
||||
return ColoredString(fmt.Sprintf("%s...", str), color.FgMagenta)
|
||||
}
|
||||
|
||||
// MarshalIntoYaml gets any json-tagged data and marshal it into yaml saving original json structure.
|
||||
// Useful for structs from 3rd-party libs without yaml tags.
|
||||
func MarshalIntoYaml(data interface{}) ([]byte, error) {
|
||||
return marshalIntoFormat(data, "yaml")
|
||||
}
|
||||
|
||||
func marshalIntoFormat(data interface{}, format string) ([]byte, error) {
|
||||
// First marshal struct->json to get the resulting structure declared by json tags
|
||||
dataJSON, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch format {
|
||||
case "json":
|
||||
return dataJSON, err
|
||||
case "yaml":
|
||||
// Use Unmarshal->Marshal hack to convert json into yaml with the original structure preserved
|
||||
var dataMirror yaml.MapSlice
|
||||
if err := yaml.Unmarshal(dataJSON, &dataMirror); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return yaml.Marshal(dataMirror)
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintf("Unsupported detailization format: %s", format))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -257,3 +257,65 @@ func TestRenderTable(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalIntoFormat(t *testing.T) {
|
||||
type innerData struct {
|
||||
Foo int `json:"foo"`
|
||||
Bar string `json:"bar"`
|
||||
Baz bool `json:"baz"`
|
||||
}
|
||||
type data struct {
|
||||
Qux int `json:"quz"`
|
||||
Quux innerData `json:"quux"`
|
||||
}
|
||||
|
||||
type scenario struct {
|
||||
input interface{}
|
||||
format string
|
||||
expected []byte
|
||||
expectedErr error
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
input: data{1, innerData{2, "foo", true}},
|
||||
format: "json",
|
||||
expected: []byte(`{
|
||||
"quz": 1,
|
||||
"quux": {
|
||||
"foo": 2,
|
||||
"bar": "foo",
|
||||
"baz": true
|
||||
}
|
||||
}`),
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
input: data{1, innerData{2, "foo", true}},
|
||||
format: "yaml",
|
||||
expected: []byte(`quz: 1
|
||||
quux:
|
||||
bar: foo
|
||||
baz: true
|
||||
foo: 2
|
||||
`),
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
input: data{1, innerData{2, "foo", true}},
|
||||
format: "xml",
|
||||
expected: nil,
|
||||
expectedErr: errors.New("Unsupported detailization format: xml"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
output, err := marshalIntoFormat(s.input, s.format)
|
||||
assert.EqualValues(t, s.expected, output)
|
||||
if s.expectedErr != nil {
|
||||
assert.EqualError(t, err, s.expectedErr.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
0
test.sh
Normal file → Executable file
0
test.sh
Normal file → Executable file
3
test/Dockerfile
Normal file
3
test/Dockerfile
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
FROM alpine:latest
|
||||
|
||||
COPY . /app
|
||||
25
test/docker-compose.yml
Normal file
25
test/docker-compose.yml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
version: "3.5"
|
||||
services:
|
||||
my-service:
|
||||
build:
|
||||
dockerfile: Dockerfile
|
||||
context: .
|
||||
command: /app/print-random-stuff.sh
|
||||
depends_on:
|
||||
- my-service2
|
||||
ports:
|
||||
- 123:321
|
||||
|
||||
my-service2:
|
||||
build:
|
||||
dockerfile: Dockerfile
|
||||
context: .
|
||||
command: /app/print-random-stuff.sh
|
||||
ports:
|
||||
- 12345:12345
|
||||
|
||||
my-service3:
|
||||
build:
|
||||
dockerfile: Dockerfile
|
||||
context: .
|
||||
command: /app/print-random-stuff.sh
|
||||
7
test/print-random-stuff.sh
Executable file
7
test/print-random-stuff.sh
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
while true
|
||||
do
|
||||
echo $((1 + $RANDOM % 10))
|
||||
sleep 1
|
||||
done
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
# building the binary
|
||||
FROM golang:1.11 as builder
|
||||
|
||||
# Add Maintainer Info
|
||||
LABEL maintainer="Jesse Duffield <jessedduffield@gmail.com>"
|
||||
|
||||
# Set the Current Working Directory inside the container
|
||||
WORKDIR /src/github.com/jesseduffield/lazydocker/test/printrandom
|
||||
|
||||
# Copy everything from the current directory to the PWD(Present Working Directory) inside the container
|
||||
COPY . .
|
||||
|
||||
# Build the package
|
||||
RUN go build
|
||||
|
||||
# putting binary into a minimal image
|
||||
FROM scratch
|
||||
|
||||
WORKDIR /root/
|
||||
|
||||
COPY --from=builder /src/github.com/jesseduffield/lazydocker/test/printrandom/printrandom .
|
||||
|
||||
# Run the executable
|
||||
CMD ["./printrandom"]
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
exitOnInterrupt()
|
||||
|
||||
for range time.Tick(time.Second / 3) {
|
||||
fmt.Println(rand.Intn(1000))
|
||||
}
|
||||
}
|
||||
|
||||
func exitOnInterrupt() {
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
<-c
|
||||
os.Exit(0)
|
||||
}()
|
||||
}
|
||||
Binary file not shown.
1
vendor/github.com/Microsoft/go-winio/.gitattributes
generated
vendored
Normal file
1
vendor/github.com/Microsoft/go-winio/.gitattributes
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
* text=auto eol=lf
|
||||
9
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
9
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
|
|
@ -1 +1,10 @@
|
|||
.vscode/
|
||||
|
||||
*.exe
|
||||
|
||||
# testing
|
||||
testdata
|
||||
|
||||
# go workspaces
|
||||
go.work
|
||||
go.work.sum
|
||||
|
|
|
|||
147
vendor/github.com/Microsoft/go-winio/.golangci.yml
generated
vendored
Normal file
147
vendor/github.com/Microsoft/go-winio/.golangci.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
linters:
|
||||
enable:
|
||||
# style
|
||||
- containedctx # struct contains a context
|
||||
- dupl # duplicate code
|
||||
- errname # erorrs are named correctly
|
||||
- nolintlint # "//nolint" directives are properly explained
|
||||
- revive # golint replacement
|
||||
- unconvert # unnecessary conversions
|
||||
- wastedassign
|
||||
|
||||
# bugs, performance, unused, etc ...
|
||||
- contextcheck # function uses a non-inherited context
|
||||
- errorlint # errors not wrapped for 1.13
|
||||
- exhaustive # check exhaustiveness of enum switch statements
|
||||
- gofmt # files are gofmt'ed
|
||||
- gosec # security
|
||||
- nilerr # returns nil even with non-nil error
|
||||
- thelper # test helpers without t.Helper()
|
||||
- unparam # unused function params
|
||||
|
||||
issues:
|
||||
exclude-dirs:
|
||||
- pkg/etw/sample
|
||||
|
||||
exclude-rules:
|
||||
# err is very often shadowed in nested scopes
|
||||
- linters:
|
||||
- govet
|
||||
text: '^shadow: declaration of "err" shadows declaration'
|
||||
|
||||
# ignore long lines for skip autogen directives
|
||||
- linters:
|
||||
- revive
|
||||
text: "^line-length-limit: "
|
||||
source: "^//(go:generate|sys) "
|
||||
|
||||
#TODO: remove after upgrading to go1.18
|
||||
# ignore comment spacing for nolint and sys directives
|
||||
- linters:
|
||||
- revive
|
||||
text: "^comment-spacings: no space between comment delimiter and comment text"
|
||||
source: "//(cspell:|nolint:|sys |todo)"
|
||||
|
||||
# not on go 1.18 yet, so no any
|
||||
- linters:
|
||||
- revive
|
||||
text: "^use-any: since GO 1.18 'interface{}' can be replaced by 'any'"
|
||||
|
||||
# allow unjustified ignores of error checks in defer statements
|
||||
- linters:
|
||||
- nolintlint
|
||||
text: "^directive `//nolint:errcheck` should provide explanation"
|
||||
source: '^\s*defer '
|
||||
|
||||
# allow unjustified ignores of error lints for io.EOF
|
||||
- linters:
|
||||
- nolintlint
|
||||
text: "^directive `//nolint:errorlint` should provide explanation"
|
||||
source: '[=|!]= io.EOF'
|
||||
|
||||
|
||||
linters-settings:
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
govet:
|
||||
enable-all: true
|
||||
disable:
|
||||
# struct order is often for Win32 compat
|
||||
# also, ignore pointer bytes/GC issues for now until performance becomes an issue
|
||||
- fieldalignment
|
||||
nolintlint:
|
||||
require-explanation: true
|
||||
require-specific: true
|
||||
revive:
|
||||
# revive is more configurable than static check, so likely the preferred alternative to static-check
|
||||
# (once the perf issue is solved: https://github.com/golangci/golangci-lint/issues/2997)
|
||||
enable-all-rules:
|
||||
true
|
||||
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md
|
||||
rules:
|
||||
# rules with required arguments
|
||||
- name: argument-limit
|
||||
disabled: true
|
||||
- name: banned-characters
|
||||
disabled: true
|
||||
- name: cognitive-complexity
|
||||
disabled: true
|
||||
- name: cyclomatic
|
||||
disabled: true
|
||||
- name: file-header
|
||||
disabled: true
|
||||
- name: function-length
|
||||
disabled: true
|
||||
- name: function-result-limit
|
||||
disabled: true
|
||||
- name: max-public-structs
|
||||
disabled: true
|
||||
# geneally annoying rules
|
||||
- name: add-constant # complains about any and all strings and integers
|
||||
disabled: true
|
||||
- name: confusing-naming # we frequently use "Foo()" and "foo()" together
|
||||
disabled: true
|
||||
- name: flag-parameter # excessive, and a common idiom we use
|
||||
disabled: true
|
||||
- name: unhandled-error # warns over common fmt.Print* and io.Close; rely on errcheck instead
|
||||
disabled: true
|
||||
# general config
|
||||
- name: line-length-limit
|
||||
arguments:
|
||||
- 140
|
||||
- name: var-naming
|
||||
arguments:
|
||||
- []
|
||||
- - CID
|
||||
- CRI
|
||||
- CTRD
|
||||
- DACL
|
||||
- DLL
|
||||
- DOS
|
||||
- ETW
|
||||
- FSCTL
|
||||
- GCS
|
||||
- GMSA
|
||||
- HCS
|
||||
- HV
|
||||
- IO
|
||||
- LCOW
|
||||
- LDAP
|
||||
- LPAC
|
||||
- LTSC
|
||||
- MMIO
|
||||
- NT
|
||||
- OCI
|
||||
- PMEM
|
||||
- PWSH
|
||||
- RX
|
||||
- SACl
|
||||
- SID
|
||||
- SMB
|
||||
- TX
|
||||
- VHD
|
||||
- VHDX
|
||||
- VMID
|
||||
- VPCI
|
||||
- WCOW
|
||||
- WIM
|
||||
1
vendor/github.com/Microsoft/go-winio/CODEOWNERS
generated
vendored
Normal file
1
vendor/github.com/Microsoft/go-winio/CODEOWNERS
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
* @microsoft/containerplat
|
||||
85
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
85
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
|
|
@ -1,4 +1,4 @@
|
|||
# go-winio
|
||||
# go-winio [](https://github.com/microsoft/go-winio/actions/workflows/ci.yml)
|
||||
|
||||
This repository contains utilities for efficiently performing Win32 IO operations in
|
||||
Go. Currently, this is focused on accessing named pipes and other file handles, and
|
||||
|
|
@ -11,12 +11,79 @@ package.
|
|||
|
||||
Please see the LICENSE file for licensing information.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of
|
||||
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information
|
||||
see the [Code of Conduct
|
||||
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
|
||||
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional
|
||||
questions or comments.
|
||||
## Contributing
|
||||
|
||||
Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe
|
||||
for another named pipe implementation.
|
||||
This project welcomes contributions and suggestions.
|
||||
Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that
|
||||
you have the right to, and actually do, grant us the rights to use your contribution.
|
||||
For details, visit [Microsoft CLA](https://cla.microsoft.com).
|
||||
|
||||
When you submit a pull request, a CLA-bot will automatically determine whether you need to
|
||||
provide a CLA and decorate the PR appropriately (e.g., label, comment).
|
||||
Simply follow the instructions provided by the bot.
|
||||
You will only need to do this once across all repos using our CLA.
|
||||
|
||||
Additionally, the pull request pipeline requires the following steps to be performed before
|
||||
mergining.
|
||||
|
||||
### Code Sign-Off
|
||||
|
||||
We require that contributors sign their commits using [`git commit --signoff`][git-commit-s]
|
||||
to certify they either authored the work themselves or otherwise have permission to use it in this project.
|
||||
|
||||
A range of commits can be signed off using [`git rebase --signoff`][git-rebase-s].
|
||||
|
||||
Please see [the developer certificate](https://developercertificate.org) for more info,
|
||||
as well as to make sure that you can attest to the rules listed.
|
||||
Our CI uses the DCO Github app to ensure that all commits in a given PR are signed-off.
|
||||
|
||||
### Linting
|
||||
|
||||
Code must pass a linting stage, which uses [`golangci-lint`][lint].
|
||||
The linting settings are stored in [`.golangci.yaml`](./.golangci.yaml), and can be run
|
||||
automatically with VSCode by adding the following to your workspace or folder settings:
|
||||
|
||||
```json
|
||||
"go.lintTool": "golangci-lint",
|
||||
"go.lintOnSave": "package",
|
||||
```
|
||||
|
||||
Additional editor [integrations options are also available][lint-ide].
|
||||
|
||||
Alternatively, `golangci-lint` can be [installed locally][lint-install] and run from the repo root:
|
||||
|
||||
```shell
|
||||
# use . or specify a path to only lint a package
|
||||
# to show all lint errors, use flags "--max-issues-per-linter=0 --max-same-issues=0"
|
||||
> golangci-lint run ./...
|
||||
```
|
||||
|
||||
### Go Generate
|
||||
|
||||
The pipeline checks that auto-generated code, via `go generate`, are up to date.
|
||||
|
||||
This can be done for the entire repo:
|
||||
|
||||
```shell
|
||||
> go generate ./...
|
||||
```
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Special Thanks
|
||||
|
||||
Thanks to [natefinch][natefinch] for the inspiration for this library.
|
||||
See [npipe](https://github.com/natefinch/npipe) for another named pipe implementation.
|
||||
|
||||
[lint]: https://golangci-lint.run/
|
||||
[lint-ide]: https://golangci-lint.run/usage/integrations/#editor-integration
|
||||
[lint-install]: https://golangci-lint.run/usage/install/#local-installation
|
||||
|
||||
[git-commit-s]: https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s
|
||||
[git-rebase-s]: https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---signoff
|
||||
|
||||
[natefinch]: https://github.com/natefinch
|
||||
|
|
|
|||
41
vendor/github.com/Microsoft/go-winio/SECURITY.md
generated
vendored
Normal file
41
vendor/github.com/Microsoft/go-winio/SECURITY.md
generated
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.7 BLOCK -->
|
||||
|
||||
## Security
|
||||
|
||||
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
|
||||
|
||||
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
|
||||
|
||||
## Reporting Security Issues
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.**
|
||||
|
||||
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
|
||||
|
||||
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
|
||||
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
|
||||
|
||||
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||
|
||||
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||
* Full paths of source file(s) related to the manifestation of the issue
|
||||
* The location of the affected source code (tag/branch/commit or direct URL)
|
||||
* Any special configuration required to reproduce the issue
|
||||
* Step-by-step instructions to reproduce the issue
|
||||
* Proof-of-concept or exploit code (if possible)
|
||||
* Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
This information will help us triage your report more quickly.
|
||||
|
||||
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
|
||||
|
||||
## Preferred Languages
|
||||
|
||||
We prefer all communications to be in English.
|
||||
|
||||
## Policy
|
||||
|
||||
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
|
||||
|
||||
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
||||
65
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
65
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
|
|
@ -1,3 +1,4 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package winio
|
||||
|
|
@ -7,15 +8,16 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
|
||||
"github.com/Microsoft/go-winio/internal/fs"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
||||
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
||||
//sys backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
||||
//sys backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
||||
|
||||
const (
|
||||
BackupData = uint32(iota + 1)
|
||||
|
|
@ -24,7 +26,7 @@ const (
|
|||
BackupAlternateData
|
||||
BackupLink
|
||||
BackupPropertyData
|
||||
BackupObjectId
|
||||
BackupObjectId //revive:disable-line:var-naming ID, not Id
|
||||
BackupReparseData
|
||||
BackupSparseBlock
|
||||
BackupTxfsData
|
||||
|
|
@ -34,14 +36,16 @@ const (
|
|||
StreamSparseAttributes = uint32(8)
|
||||
)
|
||||
|
||||
//nolint:revive // var-naming: ALL_CAPS
|
||||
const (
|
||||
WRITE_DAC = 0x40000
|
||||
WRITE_OWNER = 0x80000
|
||||
ACCESS_SYSTEM_SECURITY = 0x1000000
|
||||
WRITE_DAC = windows.WRITE_DAC
|
||||
WRITE_OWNER = windows.WRITE_OWNER
|
||||
ACCESS_SYSTEM_SECURITY = windows.ACCESS_SYSTEM_SECURITY
|
||||
)
|
||||
|
||||
// BackupHeader represents a backup stream of a file.
|
||||
type BackupHeader struct {
|
||||
//revive:disable-next-line:var-naming ID, not Id
|
||||
Id uint32 // The backup stream ID
|
||||
Attributes uint32 // Stream attributes
|
||||
Size int64 // The size of the stream in bytes
|
||||
|
|
@ -49,8 +53,8 @@ type BackupHeader struct {
|
|||
Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
|
||||
}
|
||||
|
||||
type win32StreamId struct {
|
||||
StreamId uint32
|
||||
type win32StreamID struct {
|
||||
StreamID uint32
|
||||
Attributes uint32
|
||||
Size uint64
|
||||
NameSize uint32
|
||||
|
|
@ -71,7 +75,7 @@ func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
|
|||
// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if
|
||||
// it was not completely read.
|
||||
func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
||||
if r.bytesLeft > 0 {
|
||||
if r.bytesLeft > 0 { //nolint:nestif // todo: flatten this
|
||||
if s, ok := r.r.(io.Seeker); ok {
|
||||
// Make sure Seek on io.SeekCurrent sometimes succeeds
|
||||
// before trying the actual seek.
|
||||
|
|
@ -82,16 +86,16 @@ func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
|||
r.bytesLeft = 0
|
||||
}
|
||||
}
|
||||
if _, err := io.Copy(ioutil.Discard, r); err != nil {
|
||||
if _, err := io.Copy(io.Discard, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var wsi win32StreamId
|
||||
var wsi win32StreamID
|
||||
if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hdr := &BackupHeader{
|
||||
Id: wsi.StreamId,
|
||||
Id: wsi.StreamID,
|
||||
Attributes: wsi.Attributes,
|
||||
Size: int64(wsi.Size),
|
||||
}
|
||||
|
|
@ -100,9 +104,9 @@ func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
|||
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hdr.Name = syscall.UTF16ToString(name)
|
||||
hdr.Name = windows.UTF16ToString(name)
|
||||
}
|
||||
if wsi.StreamId == BackupSparseBlock {
|
||||
if wsi.StreamID == BackupSparseBlock {
|
||||
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -147,8 +151,8 @@ func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
|
|||
return fmt.Errorf("missing %d bytes", w.bytesLeft)
|
||||
}
|
||||
name := utf16.Encode([]rune(hdr.Name))
|
||||
wsi := win32StreamId{
|
||||
StreamId: hdr.Id,
|
||||
wsi := win32StreamID{
|
||||
StreamID: hdr.Id,
|
||||
Attributes: hdr.Attributes,
|
||||
Size: uint64(hdr.Size),
|
||||
NameSize: uint32(len(name) * 2),
|
||||
|
|
@ -201,9 +205,9 @@ func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
|
|||
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
||||
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
||||
var bytesRead uint32
|
||||
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
||||
err := backupRead(windows.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
||||
if err != nil {
|
||||
return 0, &os.PathError{"BackupRead", r.f.Name(), err}
|
||||
return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err}
|
||||
}
|
||||
runtime.KeepAlive(r.f)
|
||||
if bytesRead == 0 {
|
||||
|
|
@ -216,7 +220,7 @@ func (r *BackupFileReader) Read(b []byte) (int, error) {
|
|||
// the underlying file.
|
||||
func (r *BackupFileReader) Close() error {
|
||||
if r.ctx != 0 {
|
||||
backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
||||
_ = backupRead(windows.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
||||
runtime.KeepAlive(r.f)
|
||||
r.ctx = 0
|
||||
}
|
||||
|
|
@ -240,9 +244,9 @@ func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
|
|||
// Write restores a portion of the file using the provided backup stream.
|
||||
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
||||
var bytesWritten uint32
|
||||
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
||||
err := backupWrite(windows.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
||||
if err != nil {
|
||||
return 0, &os.PathError{"BackupWrite", w.f.Name(), err}
|
||||
return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err}
|
||||
}
|
||||
runtime.KeepAlive(w.f)
|
||||
if int(bytesWritten) != len(b) {
|
||||
|
|
@ -255,7 +259,7 @@ func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
|||
// close the underlying file.
|
||||
func (w *BackupFileWriter) Close() error {
|
||||
if w.ctx != 0 {
|
||||
backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
||||
_ = backupWrite(windows.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
||||
runtime.KeepAlive(w.f)
|
||||
w.ctx = 0
|
||||
}
|
||||
|
|
@ -267,11 +271,14 @@ func (w *BackupFileWriter) Close() error {
|
|||
//
|
||||
// If the file opened was a directory, it cannot be used with Readdir().
|
||||
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
||||
winPath, err := syscall.UTF16FromString(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0)
|
||||
h, err := fs.CreateFile(path,
|
||||
fs.AccessMask(access),
|
||||
fs.FileShareMode(share),
|
||||
nil,
|
||||
fs.FileCreationDisposition(createmode),
|
||||
fs.FILE_FLAG_BACKUP_SEMANTICS|fs.FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
0,
|
||||
)
|
||||
if err != nil {
|
||||
err = &os.PathError{Op: "open", Path: path, Err: err}
|
||||
return nil, err
|
||||
|
|
|
|||
22
vendor/github.com/Microsoft/go-winio/doc.go
generated
vendored
Normal file
22
vendor/github.com/Microsoft/go-winio/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// This package provides utilities for efficiently performing Win32 IO operations in Go.
|
||||
// Currently, this package is provides support for genreal IO and management of
|
||||
// - named pipes
|
||||
// - files
|
||||
// - [Hyper-V sockets]
|
||||
//
|
||||
// This code is similar to Go's [net] package, and uses IO completion ports to avoid
|
||||
// blocking IO on system threads, allowing Go to reuse the thread to schedule other goroutines.
|
||||
//
|
||||
// This limits support to Windows Vista and newer operating systems.
|
||||
//
|
||||
// Additionally, this package provides support for:
|
||||
// - creating and managing GUIDs
|
||||
// - writing to [ETW]
|
||||
// - opening and manageing VHDs
|
||||
// - parsing [Windows Image files]
|
||||
// - auto-generating Win32 API code
|
||||
//
|
||||
// [Hyper-V sockets]: https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service
|
||||
// [ETW]: https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/event-tracing-for-windows--etw-
|
||||
// [Windows Image files]: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/work-with-windows-images
|
||||
package winio
|
||||
8
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
8
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
|
|
@ -33,7 +33,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
|||
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
|
||||
if err != nil {
|
||||
err = errInvalidEaBuffer
|
||||
return
|
||||
return ea, nb, err
|
||||
}
|
||||
|
||||
nameOffset := fileFullEaInformationSize
|
||||
|
|
@ -43,7 +43,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
|||
nextOffset := int(info.NextEntryOffset)
|
||||
if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
|
||||
err = errInvalidEaBuffer
|
||||
return
|
||||
return ea, nb, err
|
||||
}
|
||||
|
||||
ea.Name = string(b[nameOffset : nameOffset+nameLen])
|
||||
|
|
@ -52,7 +52,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
|||
if info.NextEntryOffset != 0 {
|
||||
nb = b[info.NextEntryOffset:]
|
||||
}
|
||||
return
|
||||
return ea, nb, err
|
||||
}
|
||||
|
||||
// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
|
||||
|
|
@ -67,7 +67,7 @@ func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
|
|||
eas = append(eas, ea)
|
||||
b = nb
|
||||
}
|
||||
return
|
||||
return eas, err
|
||||
}
|
||||
|
||||
func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue