@meistrari/mise-en-place 2.18.4 → 2.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/mise/tasks/registry-login.sh +124 -0
- package/package.json +49 -49
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#MISE description="Login to the internal registry. Opens the UI so you can copy your CLI secret."
|
|
3
|
+
#USAGE flag "-r --registry <registry>" default="registry.internal.tela.tools" help="Registry host to authenticate against." env="REGISTRY_HOST"
|
|
4
|
+
#USAGE flag "-u --username <username>" default="" help="Registry OIDC username. Prompted for when omitted." env="REGISTRY_USERNAME"
|
|
5
|
+
#USAGE flag "-f --force" default=#false help="Re-authenticate even if the stored credential still works."
|
|
6
|
+
#USAGE flag "--browser" negate="--no-browser" default=#true help="Open the registry UI so the CLI secret can be copied."
|
|
7
|
+
#MISE quiet=true
|
|
8
|
+
#MISE interactive=true
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
REGISTRY="${usage_registry:-${REGISTRY_HOST:-registry.internal.tela.tools}}"
|
|
13
|
+
USERNAME="${usage_username:-${REGISTRY_USERNAME:-}}"
|
|
14
|
+
|
|
15
|
+
# Harbor runs authMode: oidc_auth, so the Google password is not a valid docker
|
|
16
|
+
# credential. Harbor mints a per-user CLI secret instead, shown in the UI. This
|
|
17
|
+
# is that copy step, wrapped.
|
|
18
|
+
|
|
19
|
+
die() { echo "error: $*" >&2; exit 1; }
|
|
20
|
+
|
|
21
|
+
# `externalURL` is the VPN-only internal gateway, so an unreachable host almost
|
|
22
|
+
# always means NetBird is down rather than Harbor being broken. Say so, because
|
|
23
|
+
# the raw curl failure sends people to the wrong place.
|
|
24
|
+
check_reachable() {
|
|
25
|
+
curl -fsS --max-time 8 -o /dev/null "https://${REGISTRY}/api/v2.0/systeminfo" 2>/dev/null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Presence of a credential is not the same as a working one: CLI secrets get
|
|
29
|
+
# rotated and OIDC sessions lapse. Ask Harbor rather than reading the keychain.
|
|
30
|
+
#
|
|
31
|
+
# Checked against /v2/ (the registry API) rather than /api/v2.0/users/current,
|
|
32
|
+
# because a robot credential answers 412 on the latter — it has no user profile —
|
|
33
|
+
# which would report a perfectly good pull credential as broken. /v2/ is also
|
|
34
|
+
# the endpoint that actually governs `docker pull`, so it tests what we care about.
|
|
35
|
+
check_credential() {
|
|
36
|
+
local auth
|
|
37
|
+
auth=$(docker-credential-"$(docker_cred_store)" get <<<"${REGISTRY}" 2>/dev/null) || return 1
|
|
38
|
+
local user secret
|
|
39
|
+
user=$(jq -r '.Username // empty' <<<"$auth")
|
|
40
|
+
secret=$(jq -r '.Secret // empty' <<<"$auth")
|
|
41
|
+
[ -n "$user" ] && [ -n "$secret" ] || return 1
|
|
42
|
+
curl -fsS --max-time 8 -o /dev/null -u "${user}:${secret}" \
|
|
43
|
+
"https://${REGISTRY}/v2/" 2>/dev/null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
docker_cred_store() {
|
|
47
|
+
jq -r '.credsStore // "osxkeychain"' ~/.docker/config.json 2>/dev/null || echo osxkeychain
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
open_browser() {
|
|
51
|
+
local url="https://${REGISTRY}"
|
|
52
|
+
if command -v open >/dev/null 2>&1; then
|
|
53
|
+
open "$url"
|
|
54
|
+
elif command -v xdg-open >/dev/null 2>&1; then
|
|
55
|
+
xdg-open "$url"
|
|
56
|
+
else
|
|
57
|
+
echo "Open this manually: $url"
|
|
58
|
+
fi
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
command -v docker >/dev/null 2>&1 || die "docker is not installed"
|
|
62
|
+
command -v jq >/dev/null 2>&1 || die "jq is not installed"
|
|
63
|
+
|
|
64
|
+
check_reachable || die "cannot reach ${REGISTRY}. It is only routable over the NetBird VPN — connect and retry."
|
|
65
|
+
|
|
66
|
+
if [ "${usage_force:-false}" != "true" ] && check_credential; then
|
|
67
|
+
echo "Already logged in to ${REGISTRY}."
|
|
68
|
+
# A working credential is usually what people want, so staying logged in is
|
|
69
|
+
# the default. Offer the way out anyway: CLI secrets are per-user, and
|
|
70
|
+
# switching accounts otherwise means remembering --force.
|
|
71
|
+
if [ -t 0 ]; then
|
|
72
|
+
read -r -p "Log out and re-authenticate? [y/N] " answer || true
|
|
73
|
+
else
|
|
74
|
+
answer=""
|
|
75
|
+
fi
|
|
76
|
+
case "${answer:-}" in
|
|
77
|
+
[yY]|[yY][eE][sS])
|
|
78
|
+
# Drop the stored credential first, so a failed re-login does not
|
|
79
|
+
# leave the old account silently in place.
|
|
80
|
+
docker logout "$REGISTRY" >/dev/null
|
|
81
|
+
;;
|
|
82
|
+
*)
|
|
83
|
+
exit 0
|
|
84
|
+
;;
|
|
85
|
+
esac
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
if [ -z "$USERNAME" ]; then
|
|
89
|
+
# Registry identities come from Google (Dex), so only a work address is a
|
|
90
|
+
# plausible default. A personal git email would just be a wrong prefill.
|
|
91
|
+
default_user="$(git config --get user.email 2>/dev/null || true)"
|
|
92
|
+
case "$default_user" in
|
|
93
|
+
*@meistrari.com|*@tela.com) ;;
|
|
94
|
+
*) default_user="" ;;
|
|
95
|
+
esac
|
|
96
|
+
read -r -p "Registry username${default_user:+ [$default_user]}: " USERNAME || true
|
|
97
|
+
USERNAME="${USERNAME:-$default_user}"
|
|
98
|
+
[ -n "$USERNAME" ] || die "a username is required"
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
if [ "${usage_browser:-true}" = "true" ]; then
|
|
102
|
+
cat <<EOF
|
|
103
|
+
|
|
104
|
+
In the registry UI:
|
|
105
|
+
1. Sign in through Google (Dex) if prompted
|
|
106
|
+
2. Open the avatar menu (top right) -> User Profile
|
|
107
|
+
3. Copy the CLI secret
|
|
108
|
+
|
|
109
|
+
EOF
|
|
110
|
+
# Pause before launching, so the steps above can be read rather than being
|
|
111
|
+
# buried the instant the browser takes focus.
|
|
112
|
+
read -r -p "Press ENTER to open ${REGISTRY} in your browser... " _ || true
|
|
113
|
+
open_browser
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# -s so the secret never reaches the terminal or shell history.
|
|
117
|
+
read -r -s -p "Paste your registry CLI secret: " CLI_SECRET || true
|
|
118
|
+
echo
|
|
119
|
+
|
|
120
|
+
[ -n "$CLI_SECRET" ] || die "no CLI secret entered"
|
|
121
|
+
|
|
122
|
+
printf '%s' "$CLI_SECRET" | docker login "$REGISTRY" --username "$USERNAME" --password-stdin
|
|
123
|
+
|
|
124
|
+
echo "Logged in to ${REGISTRY} as ${USERNAME}."
|
package/package.json
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
2
|
+
"name": "@meistrari/mise-en-place",
|
|
3
|
+
"version": "2.19.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/meistrari/mise-en-place.git"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "unbuild"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"mise-en-place": "dist/cli/cli.mjs"
|
|
14
|
+
},
|
|
15
|
+
"exports": {
|
|
16
|
+
"./eslint": {
|
|
17
|
+
"types": "./dist/eslint.d.ts",
|
|
18
|
+
"import": "./dist/eslint.mjs"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
".editorconfig",
|
|
23
|
+
"Makefile",
|
|
24
|
+
"dist",
|
|
25
|
+
"scripts",
|
|
26
|
+
"tsconfig.base.json",
|
|
27
|
+
"mise"
|
|
28
|
+
],
|
|
29
|
+
"keywords": [],
|
|
30
|
+
"author": "",
|
|
31
|
+
"license": "ISC",
|
|
32
|
+
"type": "module",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/meistrari/mise-en-place/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/meistrari/mise-en-place#readme",
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@antfu/eslint-config": "6.7.3",
|
|
39
|
+
"dedent": "1.7.1",
|
|
40
|
+
"eslint": "9.39.4",
|
|
41
|
+
"eslint-plugin-diff": "2.1.7",
|
|
42
|
+
"eslint-plugin-drizzle": "0.2.3",
|
|
43
|
+
"glob": "13.0.6",
|
|
44
|
+
"jsonc-parser": "3.3.1",
|
|
45
|
+
"smol-toml": "^1.6.1",
|
|
46
|
+
"yaml": "2.8.2"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "24.12.0",
|
|
50
|
+
"unbuild": "3.6.1"
|
|
19
51
|
}
|
|
20
|
-
},
|
|
21
|
-
"files": [
|
|
22
|
-
".editorconfig",
|
|
23
|
-
"Makefile",
|
|
24
|
-
"dist",
|
|
25
|
-
"scripts",
|
|
26
|
-
"tsconfig.base.json",
|
|
27
|
-
"mise"
|
|
28
|
-
],
|
|
29
|
-
"keywords": [],
|
|
30
|
-
"author": "",
|
|
31
|
-
"license": "ISC",
|
|
32
|
-
"type": "module",
|
|
33
|
-
"bugs": {
|
|
34
|
-
"url": "https://github.com/meistrari/mise-en-place/issues"
|
|
35
|
-
},
|
|
36
|
-
"homepage": "https://github.com/meistrari/mise-en-place#readme",
|
|
37
|
-
"dependencies": {
|
|
38
|
-
"@antfu/eslint-config": "6.7.3",
|
|
39
|
-
"dedent": "1.7.1",
|
|
40
|
-
"eslint": "9.39.4",
|
|
41
|
-
"eslint-plugin-diff": "2.1.7",
|
|
42
|
-
"eslint-plugin-drizzle": "0.2.3",
|
|
43
|
-
"glob": "13.0.6",
|
|
44
|
-
"jsonc-parser": "3.3.1",
|
|
45
|
-
"smol-toml": "^1.6.1",
|
|
46
|
-
"yaml": "2.8.2"
|
|
47
|
-
},
|
|
48
|
-
"devDependencies": {
|
|
49
|
-
"@types/node": "24.12.0",
|
|
50
|
-
"unbuild": "3.6.1"
|
|
51
|
-
}
|
|
52
52
|
}
|