@remarkkpt/foundry-installer 0.1.0 → 0.1.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.
Files changed (3) hide show
  1. package/README.md +11 -5
  2. package/install.sh +79 -11
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -10,13 +10,13 @@ Num terminal do macOS, executa (precisa do Node/npm):
10
10
  npx @remarkkpt/foundry-installer
11
11
  ```
12
12
 
13
- Se o Mac ainda não tem Node/npm, usa esta alternativa:
13
+ Alternativa preparada para Macs sem Node/npm, só após publicar 0.1.1:
14
14
 
15
15
  ```sh
16
- installer="$(curl -fsSL https://unpkg.com/@remarkkpt/foundry-installer@0.1.0/install.sh)" && /bin/bash -c "$installer"
16
+ installer="$(curl -fsSL https://unpkg.com/@remarkkpt/foundry-installer@0.1.1/install.sh)" && /bin/bash -c "$installer"
17
17
  ```
18
18
 
19
- A alternativa `curl` não exige Node/npm para arrancar. Mantém-se `@0.1.0`
19
+ A alternativa `curl` não exige Node/npm para arrancar. Mantém-se `@0.1.1`
20
20
  para executar o script desta versão, sem mudar para futuras versões automaticamente.
21
21
 
22
22
  O instalador oferece preparar os
@@ -31,8 +31,14 @@ confirmação; o conteúdo existente é preservado. Uma instalação adequada é
31
31
  reutilizada. Falhas de descarga/instalação preservam a versão ativa anterior.
32
32
  O prefixo npm anteriormente documentado em `~/.local/share/foundry/node_modules`
33
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.
34
+ com SHA-256 validado e o comando existente é conservado. O launcher documentado anterior é reconhecido pelo conteúdo integral. Se a versão
35
+ for diferente, o instalador explica e pede confirmação para atualizar. Só depois
36
+ de verificar a nova CLI move o prefixo anterior para
37
+ `~/.local/share/foundry/backups/legacy-…`, juntamente com o launcher, e indica
38
+ o backup. Recusar preserva tudo. Se a ativação falhar antes de o novo comando ficar ativo, o instalador tenta repor o prefixo e o comando anteriores. Se o prefixo for reposto, regressa à localização original; caso contrário, permanece no backup indicado. Depois de o novo comando ficar ativo, não há restauro automático e o backup é conservado.
39
+ Com o launcher documentado, também se oferece atualização confirmada quando
40
+ os ficheiros diferem apesar de terem o mesmo número de versão. Sem esse launcher,
41
+ a mesma versão divergente é preservada e recusada para conferência manual.
36
42
 
37
43
  Este pacote não contém credenciais nem publica código. O acesso à Foundry
38
44
  continua limitado aos convidados do repositório privado.
package/install.sh CHANGED
@@ -52,13 +52,26 @@ version=0.1.0-alpha.1
52
52
  tag="v$version"
53
53
  base="$HOME/.local/share/foundry"
54
54
  bin="$HOME/.local/bin"
55
- for item in "$HOME/.local" "$HOME/.local/share" "$base" "$base/versions" "$bin"; do
55
+ for item in "$HOME/.local" "$HOME/.local/share" "$base" "$base/versions" "$base/backups" "$bin"; do
56
56
  [[ ! -L "$item" ]] || fail "Destino simbólico inesperado: $item. Nenhuma instalação substituída."
57
57
  done
58
58
  mkdir -p "$base/versions" "$bin"
59
59
  launcher="$bin/foundry"
60
+ legacy_launcher=false
61
+ if [[ -f "$launcher" && ! -L "$launcher" ]]; then
62
+ # Recognize the complete documented 17/10 launcher, scoped to this HOME.
63
+ # A comment or a substring alone must never authorize replacing a command.
64
+ if node - "$launcher" "$HOME" <<'JS'
65
+ const fs = require('node:fs');
66
+ const expected = '#!/bin/sh\n# Foundry uses Node 24 without changing the default Node for other tools.\nPATH="/opt/homebrew/opt/node@24/bin:$PATH"\nexport PATH\nexec /opt/homebrew/opt/node@24/bin/node ' + process.argv[3] + '/.local/share/foundry/node_modules/@remarkkpt/foundry/dist/cli.js "$@"\n';
67
+ process.exit(fs.readFileSync(process.argv[2], 'utf8') === expected ? 0 : 1);
68
+ JS
69
+ then legacy_launcher=true; fi
70
+ fi
60
71
  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.'
72
+ if [[ "$legacy_launcher" != true ]]; then
73
+ [[ -f "$launcher" && ! -L "$launcher" ]] && grep -qx '# Foundry bootstrap launcher v1' "$launcher" || fail 'Já existe outro comando em ~/.local/bin/foundry; foi preservado. Confira esse comando e guarde-o noutro local antes de repetir o instalador.'
74
+ fi
62
75
  fi
63
76
  work=$(mktemp -d "$base/versions/.prepare-XXXXXXXX")
64
77
  printf 'A conferir a versão privada %s…\n' "$tag"
@@ -72,7 +85,9 @@ JS
72
85
  ) || fail 'O manifesto da versão não tem identidade válida; instalação recusada.'
73
86
  read -r commit digest <<< "$metadata"
74
87
  download_package() {
75
- gh release download "$tag" --repo "$repository" --pattern foundry.tgz --dir "$work" || fail 'A descarga falhou; a instalação anterior foi preservada.'
88
+ if [[ ! -f "$work/foundry.tgz" ]]; then
89
+ gh release download "$tag" --repo "$repository" --pattern foundry.tgz --dir "$work" || fail 'A descarga falhou; a instalação anterior foi preservada.'
90
+ fi
76
91
  local actual
77
92
  actual=$(/usr/bin/shasum -a 256 "$work/foundry.tgz"); actual=${actual%% *}
78
93
  [[ "$actual" == "$digest" ]] || fail 'SHA-256 divergente; instalação recusada. A versão anterior foi preservada.'
@@ -81,9 +96,37 @@ download_package() {
81
96
  # installed files against the identified artifact rather than trusting version.
82
97
  legacy="$base/node_modules/@remarkkpt/foundry"
83
98
  reuse_legacy=false
99
+ update_legacy=false
100
+ legacy_backup=
101
+ legacy_moved=false
102
+ restore_legacy() {
103
+ local status=$?
104
+ if [[ "$status" != 0 && "$legacy_moved" == true ]]; then
105
+ if mv "$legacy_backup/node_modules" "$base/node_modules"; then
106
+ if [[ -f "$legacy_backup/foundry" ]]; then cp -p "$legacy_backup/foundry" "$launcher"; fi
107
+ printf 'A instalação anterior foi reposta; backup: %s\n' "$legacy_backup" >&2
108
+ else printf 'A instalação anterior está no backup: %s. Reponha node_modules antes de repetir.\n' "$legacy_backup" >&2; fi
109
+ fi
110
+ }
111
+ trap restore_legacy EXIT
84
112
  if [[ -e "$legacy" || -L "$legacy" ]]; then
85
113
  download_package
86
114
  /usr/bin/tar -xzf "$work/foundry.tgz" -C "$work" || fail 'Não foi possível conferir o pacote; a instalação anterior foi preservada.'
115
+ legacy_version=$(node - "$legacy" <<'JS'
116
+ const fs = require('node:fs'), path = require('node:path');
117
+ for (let dir = process.argv[2]; dir !== path.dirname(dir); dir = path.dirname(dir)) {
118
+ if (fs.lstatSync(dir).isSymbolicLink()) throw Error('Symbolic package directory');
119
+ }
120
+ const pkg = JSON.parse(fs.readFileSync(path.join(process.argv[2], 'package.json')));
121
+ if (pkg.name !== '@remarkkpt/foundry' || !/^[0-9]+\.[0-9]+\.[0-9]+(?:-[a-zA-Z0-9.-]+)?$/.test(pkg.version)) throw Error('Invalid previous package');
122
+ console.log(pkg.version);
123
+ JS
124
+ ) || fail 'Não foi possível identificar a instalação anterior; foi preservada. Confira o pacote antes de repetir.'
125
+ if [[ "$legacy_version" != "$version" ]]; then
126
+ printf 'A Foundry anterior é a versão %s; a versão preparada é %s. A atualização guarda a anterior num backup identificado.\n' "$legacy_version" "$version"
127
+ confirm "Atualizar Foundry para $version, guardando a instalação anterior?" || fail 'Atualização cancelada; instalação e comando anteriores preservados.'
128
+ update_legacy=true
129
+ else
87
130
  if ! node - "$work/package" "$legacy" "$version" "$base/node_modules/.bin/foundry" <<'JS'
88
131
  const fs = require('node:fs'), path = require('node:path');
89
132
  const [expected, installed, version, executable] = process.argv.slice(2);
@@ -106,16 +149,27 @@ compare();
106
149
  if (fs.realpathSync(executable) !== path.join(installed, 'dist/cli.js')) throw Error('Command points elsewhere');
107
150
  fs.accessSync(executable, fs.constants.X_OK);
108
151
  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'
152
+ then
153
+ if [[ "$legacy_launcher" == true ]]; then
154
+ printf 'A instalação anterior tem a mesma versão, mas difere da versão preparada. A atualização guarda os ficheiros anteriores num backup identificado.\n'
155
+ confirm "Atualizar Foundry para $version, guardando a instalação anterior?" || fail 'Atualização cancelada; instalação e comando anteriores preservados.'
156
+ update_legacy=true
157
+ else fail 'A instalação anterior não corresponde à versão preparada; foi preservada. Confira-a antes de voltar a instalar.'; fi
158
+ else
159
+ "$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.'
160
+ reuse_legacy=true
161
+ printf 'A instalação adequada desta versão foi preservada no prefixo npm anterior.\n'
162
+ fi
163
+ fi
113
164
  fi
165
+ if [[ "$legacy_launcher" == true && ! -d "$legacy" ]]; then fail 'O launcher anterior foi reconhecido, mas falta o pacote. Reponha a instalação anterior ou guarde o launcher antes de repetir.'; fi
114
166
  target="$base/versions/$digest"
115
167
  adequate=false
116
168
  if [[ "$reuse_legacy" == true ]]; then
117
- bin="$base/node_modules/.bin"
118
- launcher="$bin/foundry"
169
+ if [[ "$legacy_launcher" != true ]]; then
170
+ bin="$base/node_modules/.bin"
171
+ launcher="$bin/foundry"
172
+ fi
119
173
  else
120
174
  if [[ -d "$target" && ! -L "$target" && -f "$target/receipt.json" ]]; then
121
175
  if node - "$target/receipt.json" "$commit" "$digest" <<'JS'
@@ -128,7 +182,9 @@ JS
128
182
  fi
129
183
  if [[ "$adequate" == false ]]; then
130
184
  [[ ! -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.'
185
+ if [[ "$update_legacy" != true ]]; then
186
+ confirm "Instalar Foundry $version (commit $commit) uma vez neste Mac?" || fail 'Instalação cancelada.'
187
+ fi
132
188
  download_package
133
189
  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
190
  node - "$work/node_modules/@remarkkpt/foundry/package.json" "$version" <<'JS'
@@ -143,6 +199,17 @@ JS
143
199
  else
144
200
  printf 'A instalação adequada desta versão foi preservada.\n'
145
201
  fi
202
+ # Move the old prefix only after the new CLI passed all checks. The EXIT trap
203
+ # restores it if activation fails. Backups are never followed through a symlink.
204
+ if [[ "$update_legacy" == true ]]; then
205
+ [[ ! -L "$base/backups" ]] || fail 'Destino de backup simbólico; instalação anterior preservada.'
206
+ mkdir -p "$base/backups"
207
+ legacy_backup=$(mktemp -d "$base/backups/legacy-XXXXXXXX")
208
+ if [[ -f "$launcher" ]]; then cp -p "$launcher" "$legacy_backup/foundry"; fi
209
+ mv "$base/node_modules" "$legacy_backup/node_modules"
210
+ legacy_moved=true
211
+ printf 'Backup da instalação anterior: %s\n' "$legacy_backup"
212
+ fi
146
213
  # Atomic pointer replacement leaves the previous version untouched on failure.
147
214
  node - "$base" "$target" <<'JS'
148
215
  const fs = require('node:fs'), path = require('node:path');
@@ -154,6 +221,7 @@ JS
154
221
  } > "$bin/.foundry-$$"
155
222
  chmod 755 "$bin/.foundry-$$"
156
223
  mv -f "$bin/.foundry-$$" "$launcher"
224
+ legacy_moved=false
157
225
  fi
158
226
  case ":$PATH:" in
159
227
  *":$bin:"*) ;;
@@ -166,7 +234,7 @@ case ":$PATH:" in
166
234
  esac
167
235
  [[ ! -L "$profile" ]] || fail 'O perfil da shell é um link simbólico; foi preservado.'
168
236
  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
237
+ if [[ "$reuse_legacy" == true && "$legacy_launcher" != true ]]; then line='export PATH="$HOME/.local/share/foundry/node_modules/.bin:$PATH" # Foundry bootstrap'; fi
170
238
  if ! grep -Fqx "$line" "$profile" 2>/dev/null; then printf '\n%s\n' "$line" >> "$profile"; fi
171
239
  ;;
172
240
  esac
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remarkkpt/foundry-installer",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Minimal macOS bootstrap for the private Foundry distribution",
5
5
  "license": "UNLICENSED",
6
6
  "bin": { "foundry-installer": "install.sh" },