@remarkkpt/foundry-installer 0.0.0-stage → 0.1.0
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/README.md +37 -2
- package/install.sh +176 -0
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Instalador Foundry para macOS
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Este pacote público contém apenas o percurso de obtenção. A Foundry permanece
|
|
4
|
+
privada em `RemarkkPT/Foundry-V2`. É necessário um convite; um convidado pode ver
|
|
5
|
+
o código desse repositório. Cada pessoa utiliza o seu próprio login GitHub.
|
|
6
|
+
|
|
7
|
+
Num terminal do macOS, executa (precisa do Node/npm):
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npx @remarkkpt/foundry-installer
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Se o Mac ainda não tem Node/npm, usa esta alternativa:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
installer="$(curl -fsSL https://unpkg.com/@remarkkpt/foundry-installer@0.1.0/install.sh)" && /bin/bash -c "$installer"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
A alternativa `curl` não exige Node/npm para arrancar. Mantém-se `@0.1.0`
|
|
20
|
+
para executar o script desta versão, sem mudar para futuras versões automaticamente.
|
|
21
|
+
|
|
22
|
+
O instalador oferece preparar os
|
|
23
|
+
requisitos com confirmação, confere o acesso e obtém os assets `foundry.tgz` e
|
|
24
|
+
`foundry-release.json` da release privada `v0.1.0-alpha.1`. Confere o
|
|
25
|
+
SHA-256, instala uma vez por Mac e abre a seleção visual de projeto no terminal.
|
|
26
|
+
Não clona nem compila a Foundry, nem altera a visibilidade de repositórios. O script não extrai palavras-passe nem tokens; usa o teu login no GitHub CLI para confirmar o acesso e descarregar a versão privada.
|
|
27
|
+
|
|
28
|
+
A instalação usa `~/.local/share/foundry` e `~/.local/bin/foundry`. A preparação
|
|
29
|
+
do PATH, quando necessária, acrescenta uma linha ao perfil da shell apenas com
|
|
30
|
+
confirmação; o conteúdo existente é preservado. Uma instalação adequada é
|
|
31
|
+
reutilizada. Falhas de descarga/instalação preservam a versão ativa anterior.
|
|
32
|
+
O prefixo npm anteriormente documentado em `~/.local/share/foundry/node_modules`
|
|
33
|
+
também é reutilizado: os seus ficheiros são conferidos contra o pacote da release
|
|
34
|
+
com SHA-256 validado e o comando existente é conservado. Se divergirem, a cópia é
|
|
35
|
+
preservada e a instalação pára com uma explicação, sem criar outra instalação.
|
|
36
|
+
|
|
37
|
+
Este pacote não contém credenciais nem publica código. O acesso à Foundry
|
|
38
|
+
continua limitado aos convidados do repositório privado.
|
package/install.sh
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
fail() { printf '\n%s\n' "$*" >&2; exit 1; }
|
|
4
|
+
confirm() { local answer; printf '%s [s/N] ' "$1"; read -r answer || return 1; [[ "$answer" == s || "$answer" == S ]]; }
|
|
5
|
+
[[ "$(uname -s)" == Darwin ]] || fail 'Este instalador suporta macOS.'
|
|
6
|
+
[[ -t 0 && -t 1 ]] || fail 'Abra um terminal interativo para executar o instalador.'
|
|
7
|
+
# Keep existing working tools; never link/upgrade a personal installation silently.
|
|
8
|
+
export PATH="${PATH}:/opt/homebrew/bin:/usr/local/bin"
|
|
9
|
+
prepare_tool() {
|
|
10
|
+
local formula="$1"
|
|
11
|
+
printf 'É necessário preparar %s para obter e abrir a Foundry.\n' "$formula"
|
|
12
|
+
confirm "Preparar $formula com Homebrew?" || fail 'Instalação cancelada; requisito em falta.'
|
|
13
|
+
if ! command -v brew >/dev/null 2>&1; then
|
|
14
|
+
printf 'Homebrew instala ferramentas de desenvolvimento e pode pedir autorização do macOS.\n'
|
|
15
|
+
confirm 'Instalar Homebrew a partir do instalador oficial?' || fail 'Instalação cancelada; Homebrew em falta.'
|
|
16
|
+
local script
|
|
17
|
+
script=$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh) || fail 'Não foi possível obter o instalador Homebrew.'
|
|
18
|
+
/bin/bash -c "$script" || fail 'A preparação do Homebrew falhou.'
|
|
19
|
+
fi
|
|
20
|
+
command -v brew >/dev/null 2>&1 || fail 'Homebrew não ficou disponível.'
|
|
21
|
+
brew install "$formula" || fail "Não foi possível preparar $formula. A configuração existente foi preservada."
|
|
22
|
+
}
|
|
23
|
+
if ! command -v gh >/dev/null 2>&1; then prepare_tool gh; fi
|
|
24
|
+
command -v gh >/dev/null 2>&1 || fail 'GitHub CLI continua indisponível.'
|
|
25
|
+
if ! gh auth status --hostname github.com >/dev/null 2>&1; then
|
|
26
|
+
printf 'A Foundry está num repositório privado. O login é feito pelo próprio GitHub CLI.\n'
|
|
27
|
+
confirm 'Iniciar sessão no GitHub?' || fail 'Instalação cancelada; autenticação necessária.'
|
|
28
|
+
gh auth login --hostname github.com --web --git-protocol https || fail 'O login GitHub não terminou com sucesso.'
|
|
29
|
+
gh auth status --hostname github.com >/dev/null 2>&1 || fail 'A autenticação GitHub continua indisponível.'
|
|
30
|
+
fi
|
|
31
|
+
repository=RemarkkPT/Foundry-V2
|
|
32
|
+
if ! identity=$(gh repo view "$repository" --json nameWithOwner --jq .nameWithOwner 2>/dev/null) || [[ "$identity" != "$repository" ]]; then
|
|
33
|
+
fail 'Não foi possível confirmar acesso a RemarkkPT/Foundry-V2. Precisa de um convite para este repositório privado. Um convidado passa a poder ver o código. Confirme também a ligação ao GitHub e volte a tentar.'
|
|
34
|
+
fi
|
|
35
|
+
# Access is checked before any private release download or installation.
|
|
36
|
+
node_ok() { command -v node >/dev/null 2>&1 && node -e 'process.exit(process.versions.node.split(".")[0] === "24" ? 0 : 1)' >/dev/null 2>&1 && command -v npm >/dev/null 2>&1 && npm --version >/dev/null 2>&1; }
|
|
37
|
+
if ! node_ok; then
|
|
38
|
+
for node_bin in /opt/homebrew/opt/node@24/bin /usr/local/opt/node@24/bin; do
|
|
39
|
+
if [[ -x "$node_bin/node" && -x "$node_bin/npm" ]]; then export PATH="$node_bin:$PATH"; break; fi
|
|
40
|
+
done
|
|
41
|
+
fi
|
|
42
|
+
if ! node_ok; then
|
|
43
|
+
prepare_tool node@24
|
|
44
|
+
node_bin="$(brew --prefix node@24)/bin"
|
|
45
|
+
export PATH="$node_bin:$PATH"
|
|
46
|
+
fi
|
|
47
|
+
node_ok || fail 'Node 24 e npm não ficaram disponíveis; a Foundry ainda não pode abrir.'
|
|
48
|
+
if ! command -v git >/dev/null 2>&1 || ! git --version >/dev/null 2>&1; then prepare_tool git; fi
|
|
49
|
+
git --version >/dev/null 2>&1 || fail 'Git continua indisponível.'
|
|
50
|
+
node_executable=$(node -p 'process.execPath')
|
|
51
|
+
version=0.1.0-alpha.1
|
|
52
|
+
tag="v$version"
|
|
53
|
+
base="$HOME/.local/share/foundry"
|
|
54
|
+
bin="$HOME/.local/bin"
|
|
55
|
+
for item in "$HOME/.local" "$HOME/.local/share" "$base" "$base/versions" "$bin"; do
|
|
56
|
+
[[ ! -L "$item" ]] || fail "Destino simbólico inesperado: $item. Nenhuma instalação substituída."
|
|
57
|
+
done
|
|
58
|
+
mkdir -p "$base/versions" "$bin"
|
|
59
|
+
launcher="$bin/foundry"
|
|
60
|
+
if [[ -e "$launcher" || -L "$launcher" ]]; then
|
|
61
|
+
[[ -f "$launcher" && ! -L "$launcher" ]] && grep -qx '# Foundry bootstrap launcher v1' "$launcher" || fail 'Já existe outro comando em ~/.local/bin/foundry; foi preservado.'
|
|
62
|
+
fi
|
|
63
|
+
work=$(mktemp -d "$base/versions/.prepare-XXXXXXXX")
|
|
64
|
+
printf 'A conferir a versão privada %s…\n' "$tag"
|
|
65
|
+
gh release download "$tag" --repo "$repository" --pattern foundry-release.json --dir "$work" || fail 'Não foi possível obter a versão preparada. A primeira release pode ainda aguardar publicação autorizada.'
|
|
66
|
+
metadata=$(node - "$work/foundry-release.json" "$tag" "$version" <<'JS'
|
|
67
|
+
const fs = require('node:fs');
|
|
68
|
+
const m = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
|
|
69
|
+
if (m.schema_version !== 1 || m.repository !== 'RemarkkPT/Foundry-V2' || m.tag !== process.argv[3] || m.version !== process.argv[4] || m.asset !== 'foundry.tgz' || !/^[a-f0-9]{40}$/.test(m.commit) || !/^[a-f0-9]{64}$/.test(m.sha256)) throw Error('Identidade da release inválida');
|
|
70
|
+
console.log(m.commit + ' ' + m.sha256);
|
|
71
|
+
JS
|
|
72
|
+
) || fail 'O manifesto da versão não tem identidade válida; instalação recusada.'
|
|
73
|
+
read -r commit digest <<< "$metadata"
|
|
74
|
+
download_package() {
|
|
75
|
+
gh release download "$tag" --repo "$repository" --pattern foundry.tgz --dir "$work" || fail 'A descarga falhou; a instalação anterior foi preservada.'
|
|
76
|
+
local actual
|
|
77
|
+
actual=$(/usr/bin/shasum -a 256 "$work/foundry.tgz"); actual=${actual%% *}
|
|
78
|
+
[[ "$actual" == "$digest" ]] || fail 'SHA-256 divergente; instalação recusada. A versão anterior foi preservada.'
|
|
79
|
+
}
|
|
80
|
+
# The previous documented npm prefix has no bootstrap receipt. Verify its
|
|
81
|
+
# installed files against the identified artifact rather than trusting version.
|
|
82
|
+
legacy="$base/node_modules/@remarkkpt/foundry"
|
|
83
|
+
reuse_legacy=false
|
|
84
|
+
if [[ -e "$legacy" || -L "$legacy" ]]; then
|
|
85
|
+
download_package
|
|
86
|
+
/usr/bin/tar -xzf "$work/foundry.tgz" -C "$work" || fail 'Não foi possível conferir o pacote; a instalação anterior foi preservada.'
|
|
87
|
+
if ! node - "$work/package" "$legacy" "$version" "$base/node_modules/.bin/foundry" <<'JS'
|
|
88
|
+
const fs = require('node:fs'), path = require('node:path');
|
|
89
|
+
const [expected, installed, version, executable] = process.argv.slice(2);
|
|
90
|
+
// Never follow a symlink through a package directory when verifying identity.
|
|
91
|
+
for (let dir = installed; dir !== path.dirname(dir); dir = path.dirname(dir)) {
|
|
92
|
+
if (fs.lstatSync(dir).isSymbolicLink()) throw Error('Symbolic package directory');
|
|
93
|
+
}
|
|
94
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(installed, 'package.json')));
|
|
95
|
+
if (pkg.name !== '@remarkkpt/foundry' || pkg.version !== version) throw Error('Package identity differs');
|
|
96
|
+
function compare(relative = '') {
|
|
97
|
+
const source = path.join(expected, relative), target = path.join(installed, relative);
|
|
98
|
+
const a = fs.lstatSync(source), b = fs.lstatSync(target);
|
|
99
|
+
if (a.isDirectory() && b.isDirectory()) {
|
|
100
|
+
for (const entry of fs.readdirSync(source)) compare(path.join(relative, entry));
|
|
101
|
+
} else if (!a.isFile() || !b.isFile() || !fs.readFileSync(source).equals(fs.readFileSync(target))) {
|
|
102
|
+
throw Error('Installed file differs: ' + relative);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
compare();
|
|
106
|
+
if (fs.realpathSync(executable) !== path.join(installed, 'dist/cli.js')) throw Error('Command points elsewhere');
|
|
107
|
+
fs.accessSync(executable, fs.constants.X_OK);
|
|
108
|
+
JS
|
|
109
|
+
then fail 'A instalação anterior não corresponde à versão preparada; foi preservada. Confira-a antes de voltar a instalar.'; fi
|
|
110
|
+
"$node_executable" "$legacy/dist/cli.js" --help >/dev/null 2>&1 || fail 'A instalação anterior não arranca; foi preservada. Confira-a antes de voltar a instalar.'
|
|
111
|
+
reuse_legacy=true
|
|
112
|
+
printf 'A instalação adequada desta versão foi preservada no prefixo npm anterior.\n'
|
|
113
|
+
fi
|
|
114
|
+
target="$base/versions/$digest"
|
|
115
|
+
adequate=false
|
|
116
|
+
if [[ "$reuse_legacy" == true ]]; then
|
|
117
|
+
bin="$base/node_modules/.bin"
|
|
118
|
+
launcher="$bin/foundry"
|
|
119
|
+
else
|
|
120
|
+
if [[ -d "$target" && ! -L "$target" && -f "$target/receipt.json" ]]; then
|
|
121
|
+
if node - "$target/receipt.json" "$commit" "$digest" <<'JS'
|
|
122
|
+
const fs = require('node:fs'); const m = JSON.parse(fs.readFileSync(process.argv[2]));
|
|
123
|
+
process.exit(m.commit === process.argv[3] && m.sha256 === process.argv[4] ? 0 : 1);
|
|
124
|
+
JS
|
|
125
|
+
then
|
|
126
|
+
if "$node_executable" "$target/node_modules/@remarkkpt/foundry/dist/cli.js" --help >/dev/null 2>&1; then adequate=true; fi
|
|
127
|
+
fi
|
|
128
|
+
fi
|
|
129
|
+
if [[ "$adequate" == false ]]; then
|
|
130
|
+
[[ ! -e "$target" && ! -L "$target" ]] || fail 'A cópia desta versão não passou a verificação; foi preservada para inspeção.'
|
|
131
|
+
confirm "Instalar Foundry $version (commit $commit) uma vez neste Mac?" || fail 'Instalação cancelada.'
|
|
132
|
+
download_package
|
|
133
|
+
npm install --prefix "$work" --offline --ignore-scripts --no-save --no-audit --no-fund "$work/foundry.tgz" || fail 'A instalação falhou; a versão anterior foi preservada.'
|
|
134
|
+
node - "$work/node_modules/@remarkkpt/foundry/package.json" "$version" <<'JS'
|
|
135
|
+
const fs = require('node:fs'); const p = JSON.parse(fs.readFileSync(process.argv[2]));
|
|
136
|
+
if (p.name !== '@remarkkpt/foundry' || p.version !== process.argv[3]) throw Error('Pacote divergente');
|
|
137
|
+
JS
|
|
138
|
+
"$node_executable" "$work/node_modules/@remarkkpt/foundry/dist/cli.js" --help >/dev/null || fail 'O programa instalado não arranca; a versão anterior foi preservada.'
|
|
139
|
+
node - "$work/receipt.json" "$commit" "$digest" <<'JS'
|
|
140
|
+
require('node:fs').writeFileSync(process.argv[2], JSON.stringify({commit:process.argv[3],sha256:process.argv[4]}));
|
|
141
|
+
JS
|
|
142
|
+
mv "$work" "$target"
|
|
143
|
+
else
|
|
144
|
+
printf 'A instalação adequada desta versão foi preservada.\n'
|
|
145
|
+
fi
|
|
146
|
+
# Atomic pointer replacement leaves the previous version untouched on failure.
|
|
147
|
+
node - "$base" "$target" <<'JS'
|
|
148
|
+
const fs = require('node:fs'), path = require('node:path');
|
|
149
|
+
const link = path.join(process.argv[2], '.current-' + process.pid);
|
|
150
|
+
fs.symlinkSync(process.argv[3], link); fs.renameSync(link, path.join(process.argv[2], 'current'));
|
|
151
|
+
JS
|
|
152
|
+
{
|
|
153
|
+
printf '#!/bin/bash\n# Foundry bootstrap launcher v1\nexec %q %q "$@"\n' "$node_executable" "$base/current/node_modules/@remarkkpt/foundry/dist/cli.js"
|
|
154
|
+
} > "$bin/.foundry-$$"
|
|
155
|
+
chmod 755 "$bin/.foundry-$$"
|
|
156
|
+
mv -f "$bin/.foundry-$$" "$launcher"
|
|
157
|
+
fi
|
|
158
|
+
case ":$PATH:" in
|
|
159
|
+
*":$bin:"*) ;;
|
|
160
|
+
*)
|
|
161
|
+
confirm 'Preparar o comando foundry nos próximos terminais?' || fail 'Instalação preparada; falta confirmar a preparação do comando foundry. Pode repetir este instalador.'
|
|
162
|
+
case "${SHELL:-/bin/zsh}" in
|
|
163
|
+
*/zsh) profile="$HOME/.zprofile" ;;
|
|
164
|
+
*/bash) profile="$HOME/.bash_profile" ;;
|
|
165
|
+
*) fail 'Shell não suportada para preparar o PATH; instalação preservada.' ;;
|
|
166
|
+
esac
|
|
167
|
+
[[ ! -L "$profile" ]] || fail 'O perfil da shell é um link simbólico; foi preservado.'
|
|
168
|
+
line='export PATH="$HOME/.local/bin:$PATH" # Foundry bootstrap'
|
|
169
|
+
if [[ "$reuse_legacy" == true ]]; then line='export PATH="$HOME/.local/share/foundry/node_modules/.bin:$PATH" # Foundry bootstrap'; fi
|
|
170
|
+
if ! grep -Fqx "$line" "$profile" 2>/dev/null; then printf '\n%s\n' "$line" >> "$profile"; fi
|
|
171
|
+
;;
|
|
172
|
+
esac
|
|
173
|
+
export PATH="$bin:$PATH"
|
|
174
|
+
printf 'A abrir o assistente Foundry e a seleção de projeto…\n'
|
|
175
|
+
"$launcher" || fail 'O assistente não terminou com sucesso; a instalação foi preservada.'
|
|
176
|
+
printf 'Foundry instalada e assistente terminado. Comando disponível: foundry (novo terminal).\n'
|
package/package.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remarkkpt/foundry-installer",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
}
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Minimal macOS bootstrap for the private Foundry distribution",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"bin": { "foundry-installer": "install.sh" },
|
|
7
|
+
"files": ["install.sh", "README.md"],
|
|
8
|
+
"publishConfig": { "access": "public" }
|
|
9
|
+
}
|