Skip to content

Commit 766878e

Browse files
committed
Moving to go1.17.9
Updating all dependencies Using up to date gclpr backend Linting
1 parent 5fe5776 commit 766878e

File tree

338 files changed

+80050
-6867
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

338 files changed

+80050
-6867
lines changed

.gitignore

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,15 @@
88
*.prof
99
*.mprof
1010
*.log
11-
*.syso
1211
*.7z
13-
*.zip
14-
*.minisig
12+
*.syso
1513

16-
release/
1714
build/
18-
bin/
1915

2016
# build specific
2117
misc/version.go
22-
resources.rc
23-
manifest.xml
18+
cmd/*/resources.rc
19+
cmd/*/manifest.xml
20+
wsl-ssh-agent.zip
21+
wsl-ssh-agent.zip.minisig
22+
wsl-ssh-agent.json

CMakeLists.txt

Lines changed: 62 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ endif()
3737
set(DEST_DIR "${PROJECT_SOURCE_DIR}/bin")
3838
message(STATUS "Installation directory: ${DEST_DIR}")
3939

40-
find_package(Go 1.15 REQUIRED)
40+
set(GO_MIN_REQURED_VERSION 1.17)
41+
find_package(Go ${GO_MIN_REQURED_VERSION} REQUIRED)
4142
find_package(Git REQUIRED)
4243

4344
# Make sure we setup go properly
@@ -58,8 +59,8 @@ endif()
5859

5960
# Project version number
6061
set(PRJ_VERSION_Major "1")
61-
set(PRJ_VERSION_Minor "5")
62-
set(PRJ_VERSION_Patch "2")
62+
set(PRJ_VERSION_Minor "6")
63+
set(PRJ_VERSION_Patch "0")
6364

6465
if (EXISTS "${PROJECT_SOURCE_DIR}/.git" AND IS_DIRECTORY "${PROJECT_SOURCE_DIR}/.git")
6566
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/cmake/githash.sh ${GIT_EXECUTABLE}
@@ -74,12 +75,15 @@ else()
7475
set(GIT_HASH "no-git")
7576
endif()
7677
configure_file("${PROJECT_SOURCE_DIR}/cmake/version.go.in" "${PROJECT_SOURCE_DIR}/misc/version.go")
77-
configure_file("${PROJECT_SOURCE_DIR}/cmake/agent.rc.in" "${PROJECT_SOURCE_DIR}/resources.rc")
78-
configure_file("${PROJECT_SOURCE_DIR}/cmake/agent.xml.in" "${PROJECT_SOURCE_DIR}/manifest.xml")
78+
configure_file("${PROJECT_SOURCE_DIR}/cmake/agent.rc.in" "${PROJECT_SOURCE_DIR}/cmd/agent/resources.rc")
79+
configure_file("${PROJECT_SOURCE_DIR}/cmake/agent.xml.in" "${PROJECT_SOURCE_DIR}/cmd/agent/manifest.xml")
80+
configure_file("${PROJECT_SOURCE_DIR}/cmake/wsl-ssh-agent.json.in" "${PROJECT_SOURCE_DIR}/wsl-ssh-agent.json")
81+
82+
# for user convinience
83+
execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../../jstarks/npiperelay/npiperelay.exe ${PROJECT_BINARY_DIR})
7984

8085
# distribute history
8186
execute_process(COMMAND ${CMAKE_COMMAND} -E env ${GIT_EXECUTABLE} changelog --all --tag "v${PRJ_VERSION_Major}.${PRJ_VERSION_Minor}.${PRJ_VERSION_Patch}" --stdout OUTPUT_FILE ${PROJECT_BINARY_DIR}/changelog.txt ERROR_QUIET)
82-
install(FILES ${PROJECT_BINARY_DIR}/changelog.txt DESTINATION ${DEST_DIR} CONFIGURATIONS Release)
8387

8488
message(STATUS "Building version \"${PRJ_VERSION_Major}.${PRJ_VERSION_Minor}.${PRJ_VERSION_Patch}\" git \"${GIT_HASH}\"")
8589

@@ -94,33 +98,55 @@ endif()
9498
# Some contexts dependent settings
9599
########################################################################################################
96100

101+
list(APPEND GO_ENV
102+
GOPATH=${GO_PATH}
103+
GOOS=${CROSS_GOOS}
104+
GOARCH=${CROSS_GOARCH}
105+
)
106+
97107
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
98-
set(TEST_RUN_ARGS "-v")
108+
set(GO_ARGS "-mod=mod")
99109
else()
100-
set(GO_MOD_ARGS "-mod=vendor")
110+
set(GO_ARGS "-mod=vendor")
101111
endif()
102112

103113
########################################################################################################
104114
# main target - always out of date, "go build" will figure it out
105115
########################################################################################################
106116

117+
add_custom_target(release
118+
DEPENDS ${PROJECT_SOURCE_DIR}/wsl-ssh-agent.zip
119+
${PROJECT_SOURCE_DIR}/wsl-ssh-agent.json
120+
COMMAND ${CMAKE_SOURCE_DIR}/cmake/sign.sh
121+
COMMENT "Building release..."
122+
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}")
123+
124+
add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/wsl-ssh-agent.zip
125+
DEPENDS ${PROJECT_BINARY_DIR}/changelog.txt
126+
${PROJECT_BINARY_DIR}/npiperelay${CMAKE_EXECUTABLE_SUFFIX}
127+
${PROJECT_BINARY_DIR}/wsl-ssh-agent-gui${CMAKE_EXECUTABLE_SUFFIX}
128+
COMMAND ${CMAKE_COMMAND} -E tar "cfv" ${PROJECT_SOURCE_DIR}/wsl-ssh-agent.zip --format=zip
129+
changelog.txt npiperelay${CMAKE_EXECUTABLE_SUFFIX} wsl-ssh-agent-gui${CMAKE_EXECUTABLE_SUFFIX}
130+
COMMENT "Archiving release..."
131+
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}")
132+
107133
add_custom_target(bin_agent ALL
108-
DEPENDS ${PROJECT_SOURCE_DIR}/resources.syso
109-
COMMAND GOPATH=${GO_PATH} GO111MODULE=on GOOS=${CROSS_GOOS} GOARCH=${CROSS_GOARCH}
110-
${GO_EXECUTABLE} build -trimpath -o ${PROJECT_BINARY_DIR}/wsl-ssh-agent-gui${CMAKE_EXECUTABLE_SUFFIX}
134+
DEPENDS ${PROJECT_SOURCE_DIR}/cmd/agent/resources.syso
135+
BYPRODUCTS ${PROJECT_BINARY_DIR}/wsl-ssh-agent-gui${CMAKE_EXECUTABLE_SUFFIX}
136+
COMMAND ${GO_ENV} ${GO_EXECUTABLE} build -trimpath -o ${PROJECT_BINARY_DIR}/wsl-ssh-agent-gui${CMAKE_EXECUTABLE_SUFFIX}
111137
-ldflags='-H=windowsgui'
112-
${GO_MOD_ARGS}
138+
${GO_ARGS}
139+
./cmd/agent
113140
COMMENT "Building wsl-ssh-agent-gui..."
114141
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}")
115-
install(PROGRAMS ${PROJECT_BINARY_DIR}/wsl-ssh-agent-gui${CMAKE_EXECUTABLE_SUFFIX} DESTINATION ${DEST_DIR} CONFIGURATIONS Release)
116142

117-
add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/resources.syso
118-
DEPENDS ${PROJECT_SOURCE_DIR}/resources.rc
119-
${PROJECT_SOURCE_DIR}/manifest.xml
120-
${PROJECT_SOURCE_DIR}/icon.ico
143+
add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/cmd/agent/resources.syso
144+
DEPENDS ${PROJECT_SOURCE_DIR}/cmd/agent/resources.rc
145+
${PROJECT_SOURCE_DIR}/cmd/agent/manifest.xml
146+
${PROJECT_SOURCE_DIR}/cmd/agent/icon.ico
121147
COMMAND ${CMAKE_RC_COMPILER} -O coff
122-
-o ${PROJECT_SOURCE_DIR}/resources.syso
123-
-i ${PROJECT_SOURCE_DIR}/resources.rc
148+
-o ${PROJECT_SOURCE_DIR}/cmd/agent/resources.syso
149+
-i ${PROJECT_SOURCE_DIR}/cmd/agent/resources.rc
124150
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
125151
COMMENT "Building wsl-ssh-agent-gui resources...")
126152

@@ -134,32 +160,16 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
134160
# Go linter
135161
########################################################################################################
136162

137-
138-
# Running linters on main target
139-
find_program(GO_LINTER golangci-lint PATHS ${PROJECT_BINARY_DIR} NO_DEFAULT_PATH)
140-
if(NOT GO_LINTER)
141-
set(golangci-lint-ver "1.33.0")
142-
message(STATUS "Preparing golangci-lint ${golangci-lint-ver}")
143-
file(DOWNLOAD "https://github.com/golangci/golangci-lint/releases/download/v${golangci-lint-ver}/golangci-lint-${golangci-lint-ver}-linux-amd64.tar.gz" ${PROJECT_BINARY_DIR}/golangci-lint.tar.gz
144-
INACTIVITY_TIMEOUT 60
145-
TIMEOUT 300
146-
STATUS DOWNLOAD_RES
147-
EXPECTED_HASH SHA256=e2d6082f1df53c5d2c280765000f9e82783ea909ba419c6c4e172936b076031e)
148-
list(GET DOWNLOAD_RES 0 RES)
149-
list(GET DOWNLOAD_RES 1 MSG)
150-
if(RES)
151-
message(FATAL_ERROR "${MSG} : ${RES}")
152-
endif()
153-
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf golangci-lint.tar.gz WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
154-
execute_process(COMMAND ${CMAKE_COMMAND} -E copy golangci-lint-${golangci-lint-ver}-linux-amd64/golangci-lint golangci-lint WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
155-
execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory golangci-lint-${golangci-lint-ver}-linux-amd64 WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
156-
execute_process(COMMAND ${CMAKE_COMMAND} -E remove golangci-lint.tar.gz WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
157-
set(GO_LINTER ${PROJECT_BINARY_DIR}/golangci-lint)
158-
endif()
163+
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/staticcheck
164+
COMMAND GOPATH=${GO_PATH} GOOS= GOARCH= ${GO_EXECUTABLE} build ${GO_MOD_ARGS} -o ${PROJECT_BINARY_DIR}/staticcheck
165+
honnef.co/go/tools/cmd/staticcheck
166+
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
167+
COMMENT "Building staticcheck executable...")
159168

160169
add_custom_target(lint
161-
COMMAND GOPATH=${GO_PATH} GO111MODULE=on GOOS=${CROSS_GOOS} GOARCH=${CROSS_GOARCH} ${GO_LINTER} run
162-
COMMENT "Mega-linting project with ${GO_LINTER}..."
170+
DEPENDS ${PROJECT_BINARY_DIR}/staticcheck
171+
COMMAND ${PROJECT_BINARY_DIR}/staticcheck -f stylish -tests=false ./...
172+
COMMENT "Linting project..."
163173
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}")
164174

165175
########################################################################################################
@@ -168,14 +178,21 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
168178

169179
add_custom_target(deps_tidy
170180
SOURCES ${PROJECT_SOURCE_DIR}/go.mod
171-
COMMAND GOPATH=${GO_PATH} GO111MODULE=on GOOS=${CROSS_GOOS} GOARCH=${CROSS_GOARCH} ${GO_EXECUTABLE} mod tidy
181+
COMMAND ${GO_ENV} ${GO_EXECUTABLE} mod tidy -compat=${GO_MIN_REQURED_VERSION}
172182
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
173183
COMMENT "Cleaning dependencies (go mod tidy)"
174184
VERBATIM)
175185

186+
add_custom_target(deps_download
187+
SOURCES ${PROJECT_SOURCE_DIR}/go.mod
188+
COMMAND ${GO_ENV} ${GO_EXECUTABLE} mod download all
189+
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
190+
COMMENT "Downloading dependencies (go mod download)"
191+
VERBATIM)
192+
176193
add_custom_target(deps_vendor
177194
SOURCES ${PROJECT_SOURCE_DIR}/go.mod
178-
COMMAND GOPATH=${GO_PATH} GO111MODULE=on GOOS=${CROSS_GOOS} GOARCH=${CROSS_GOARCH} ${GO_EXECUTABLE} mod vendor
195+
COMMAND ${GO_ENV} ${GO_EXECUTABLE} mod vendor
179196
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
180197
COMMENT "Creating vendor directory (go mod vendor)"
181198
VERBATIM)

README.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,19 @@
1010
<hr>
1111
</p>
1212

13-
Windows 10 has very convenient `ssh-agent` service (with support for persistence and Windows security). Unfortunately it is not accessible from WSL. This project aims to correct this situation by enabling access to SSH keys held by Windows own `ssh-agent` service from inside the [Windows Subsystem for Linux](https://msdn.microsoft.com/en-us/commandline/wsl/about).
13+
Windows has very convenient `ssh-agent` service (with support for persistence and Windows security). Unfortunately it is not accessible from WSL. This project aims to correct this situation by enabling access to SSH keys held by Windows own `ssh-agent` service from inside the [Windows Subsystem for Linux](https://msdn.microsoft.com/en-us/commandline/wsl/about).
1414

1515
My first attempt - [ssh-agent-wsl](https://github.com/rupor-github/ssh-agent-wsl) was successful, but due to Windows interop restrictions it required elaborate life-time management on the WSL side. Starting with build 17063 (which was many updates ago) Windows implemented AF_UNIX sockets. This makes it possible to remove all trickery from WSL side greatly simplifying everything.
1616

1717
**NOTE:** If you need access to more functionality (smard cards, identity management) provided by [GnuPG](https://www.gnupg.org/) set of tools on Windows or if you are looking for compatibility with wider set of utilities, like Git for Windows, Putty, Cygwin - you may want to take a look at [win-gpg-agent](https://github.com/rupor-github/win-gpg-agent) instead.
1818

19-
`wsl-ssh-agent-gui.exe` is a simple "notification tray" applet which maintains AF_UNIX ssh-agent compatible socket on Windows end. It proxes all requests from this socket to ssh-agent.exe via named pipe. The only thing required on WSL end for it to work is to make sure that WSL `SSH_AGENT_SOCK` points to proper socket path. The same socket could be shared by any/all WSL sessions.
19+
`wsl-ssh-agent-gui.exe` is a simple "notification tray" applet which maintains AF_UNIX ssh-agent compatible socket on Windows end. It proxies all requests from this socket to ssh-agent.exe via named pipe. The only thing required on WSL end for it to work is to make sure that WSL `SSH_AGENT_SOCK` points to proper socket path. The same socket could be shared by any/all WSL sessions.
2020

2121
As an additional bonus `wsl-ssh-agent-gui.exe` could work as remote clipboard server so you could send your clipboard from tmux or neovim remote session back to your windows box over SSH secured connection easily.
2222

2323
**NOTE: BREAKING CHANGE** Version 1.5.0 introduces breaking change. If you were not using `wsl-ssh-agent-gui.exe` as `lemonade` clipboard backend - this should not concern you at the slightest. Otherwise lemonade support no longer - it has been replaced with [gclpr](https://github.com/rupor-github/gclpr) which is more secure.
2424

25+
**NOTE: BREAKING CHANGE** Version 1.6.0 introduces breaking change. If you were not using `wsl-ssh-agent-gui.exe` as `gclpr` clipboard backend - this should not concern you at the slightest. Otherwise starting with v1.1.0 gclpr server backend (included with v1.6.0) enforces protocol visioning and may require upgrade of gclpr tools.
2526

2627
**SECURITY NOTICE:** All the usual security caveats applicable to WSL apply. Most importantly, all interaction with the Win32 world happens with the credentials of the user who started the WSL environment. In practice, *if you allow someone else to log in to your WSL environment remotely, they may be able to access the SSH keys stored in your ssh-agent.* This is a fundamental feature of WSL; if you are not sure of what you're doing, do not allow remote access to your WSL environment (i.e. by starting an SSH server).
2728

@@ -31,11 +32,22 @@ starting with 1809 - beginning with insider build 17063 and would not work on ol
3132

3233
## Installation
3334

34-
### From binaries
35+
```
36+
scoop install https://github.com/rupor-github/wsl-ssh-agent/releases/latest/download/wsl-ssh-agent.json
37+
```
38+
and updating:
39+
```
40+
scoop update wsl-ssh-agent
41+
```
42+
43+
Alternatively download from the [releases page](https://github.com/rupor-github/wsl-ssh-agent/releases) and unpack it in a convenient location.
3544

36-
Download from the [releases page](https://github.com/rupor-github/wsl-ssh-agent/releases) and unpack it in a convenient location.
45+
Starting with v1.5.1 releases are packed with zip and signed with [minisign](https://jedisct1.github.io/minisign/). Here is public key for verification:
3746

38-
Starting with v1.5.1 releases are packed with zip and signed with [minisign](https://jedisct1.github.io/minisign/). Here is public key for verification: ![key](docs/build_key.png) RWTNh1aN8DrXq26YRmWO3bPBx4m8jBATGXt4Z96DF4OVSzdCBmoAU+Vq
47+
<p>
48+
<img src="docs/build_key.svg" style="vertical-align:middle; width:15%" align="absmiddle"/>
49+
<span style="vertical-align:middle;">&nbsp;&nbsp;RWTNh1aN8DrXq26YRmWO3bPBx4m8jBATGXt4Z96DF4OVSzdCBmoAU+Vq</span>
50+
</p>
3951

4052
## Usage
4153

cmake/sign.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/bash
2+
set -e
3+
gopass show -o build/minisign | minisign -S -s ${HOME}/.minisign/build.key -c "wsl-ssh-agent release signature" -m wsl-ssh-agent.zip
4+
sed -i "s/__CURRENT_HASH__/$(sha256sum -z wsl-ssh-agent.zip | awk '{ print $1; }')/g" wsl-ssh-agent.json

cmake/wsl-ssh-agent.json.in

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"version": "@PRJ_VERSION_Major@.@PRJ_VERSION_Minor@.@PRJ_VERSION_Patch@",
3+
"description": "Simple set of tools to make working with SSH keys easier on Windows.",
4+
"homepage": "https://github.com/rupor-github/wsl-ssh-agent",
5+
"license": "GPL-3.0",
6+
"architecture": {
7+
"64bit": {
8+
"url": "https://github.com/rupor-github/wsl-ssh-agent/releases/download/v@PRJ_VERSION_Major@.@PRJ_VERSION_Minor@.@PRJ_VERSION_Patch@/wsl-ssh-agent.zip",
9+
"hash": "__CURRENT_HASH__"
10+
}
11+
},
12+
"shortcuts": [
13+
[
14+
"wsl-ssh-agent-gui.exe",
15+
"wsl-ssh-agent"
16+
]
17+
],
18+
"checkver": {
19+
"github": "https://github.com/rupor-github/wsl-ssh-agent"
20+
},
21+
"autoupdate": {
22+
"architecture": {
23+
"64bit": {
24+
"url": "https://github.com/rupor-github/wsl-ssh-agent/releases/download/v$version/wsl-ssh-agent.zip"
25+
}
26+
}
27+
}
28+
}
File renamed without changes.

gui.go renamed to cmd/agent/main.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/Microsoft/go-winio"
2121
si "github.com/allan-simon/go-singleinstance"
2222
clip "github.com/rupor-github/gclpr/server"
23+
cliputil "github.com/rupor-github/gclpr/util"
2324
"golang.org/x/sys/windows"
2425

2526
"github.com/rupor-github/wsl-ssh-agent/misc"
@@ -259,8 +260,7 @@ func clipServe() error {
259260
return err
260261
}
261262

262-
var pkeys map[[32]byte]struct{}
263-
pkeys, err = util.ReadTrustedKeys(home)
263+
pkeys, err := cliputil.ReadTrustedKeys(home)
264264
if err != nil {
265265
if !os.IsNotExist(err) {
266266
return err
@@ -272,7 +272,8 @@ func clipServe() error {
272272
// we have possible clients for remote clipboard
273273
clipHelp = fmt.Sprintf("gclpr is serving %d key(s) on port %d", len(pkeys), clipPort)
274274
go func() {
275-
if err := clip.Serve(clipCtx, clipPort, clipLE, pkeys); err != nil {
275+
compatibleMagic := []byte{'g', 'c', 'l', 'p', 'r', 1, 1, 0}
276+
if err := clip.Serve(clipCtx, clipPort, clipLE, pkeys, compatibleMagic); err != nil {
276277
log.Printf("gclpr serve() returned error: %s", err.Error())
277278
clipHelp = "gclpr is not served"
278279
}

docs/build_key.png

-12 KB
Binary file not shown.

go.mod

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
module github.com/rupor-github/wsl-ssh-agent
22

3-
go 1.13
3+
go 1.17
44

55
require (
6-
github.com/Microsoft/go-winio v0.4.14
7-
github.com/allan-simon/go-singleinstance v0.0.0-20160830203053-79edcfdc2dfc
8-
github.com/rupor-github/gclpr v1.0.5
9-
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037
6+
github.com/Microsoft/go-winio v0.5.2
7+
github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37
8+
github.com/rupor-github/gclpr v1.2.0
9+
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150
10+
honnef.co/go/tools v0.3.0
11+
)
12+
13+
require (
14+
github.com/BurntSushi/toml v0.4.1 // indirect
15+
github.com/atotto/clipboard v0.1.4 // indirect
16+
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
17+
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
18+
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect
19+
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
20+
golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a // indirect
21+
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
1022
)

0 commit comments

Comments
 (0)