@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.
Files changed (3) hide show
  1. package/README.md +37 -2
  2. package/install.sh +176 -0
  3. package/package.json +7 -4
package/README.md CHANGED
@@ -1,3 +1,38 @@
1
- # Temporary Holding Version
1
+ # Instalador Foundry para macOS
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
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
+ }