chore(release): standardize publish and update workflow
Some checks are pending
CI / build (push) Waiting to run

This commit is contained in:
Jesse Freitas 2026-03-16 15:27:07 -03:00
parent 365906a58d
commit d1725d4af9
3 changed files with 228 additions and 0 deletions

23
AGENTS.md Normal file
View file

@ -0,0 +1,23 @@
# AGENTS
## Publicacao e atualizacao do pacote
- Sempre que a tarefa envolver publicar ou atualizar este community node, use `python scripts/publish_and_update.py`.
- Nunca rode `npm publish` diretamente, exceto se o usuario pedir explicitamente para ignorar esse fluxo.
- Para atualizar uma instalacao local do n8n baseada em diretorio, use `--target`.
- Para atualizar uma instalacao em Docker Compose, use `--docker-service` e, quando necessario, `--docker-compose-file`.
- Se houver bloqueio externo de politica, credencial, npm, Docker ou ambiente, explique isso claramente ao usuario e mantenha o script como fluxo padrao.
## Exemplos
Publicar e atualizar um n8n em diretorio local:
```bash
python scripts/publish_and_update.py --token "<NPM_TOKEN>" --target "C:\\caminho\\do\\n8n"
```
Publicar e atualizar um n8n em Docker Compose:
```bash
python scripts/publish_and_update.py --token "<NPM_TOKEN>" --docker-service n8n --docker-compose-file "C:\\caminho\\docker-compose.yml"
```

View file

@ -57,6 +57,32 @@ Para instalar o pacote publicado no n8n:
npm install @jessefreitas/n8n-nodes-mega
```
## Publicacao e update
O fluxo padrao para publicar uma nova versao e atualizar uma instalacao do n8n deve usar:
```bash
python scripts/publish_and_update.py
```
Exemplo para publicar e atualizar um n8n local por diretorio:
```bash
python scripts/publish_and_update.py --token "<NPM_TOKEN>" --target "C:\\caminho\\do\\n8n"
```
Exemplo para publicar e atualizar um n8n em Docker Compose:
```bash
python scripts/publish_and_update.py --token "<NPM_TOKEN>" --docker-service n8n --docker-compose-file "C:\\caminho\\docker-compose.yml"
```
Se voce ja publicou a versao e quer apenas atualizar o ambiente:
```bash
python scripts/publish_and_update.py --skip-publish --version 0.4.12 --target "C:\\caminho\\do\\n8n"
```
## Credenciais
Crie uma credencial `Mega API` no n8n com:

View file

@ -0,0 +1,179 @@
import argparse
import getpass
import os
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
PACKAGE_NAME = "@jessefreitas/n8n-nodes-mega"
def resolve_npm() -> str:
if sys.platform == "win32":
return shutil.which("npm.cmd") or shutil.which("npm") or "npm"
return shutil.which("npm") or "npm"
def run(command: list[str], cwd: Path, env: dict[str, str] | None = None) -> None:
result = subprocess.run(command, cwd=cwd, env=env, check=False)
if result.returncode != 0:
raise SystemExit(result.returncode)
def build_temp_npmrc(token: str) -> tuple[Path, Path]:
temp_dir = Path(tempfile.mkdtemp(prefix="npm-publish-"))
npmrc = temp_dir / ".npmrc"
npmrc.write_text(
f"//registry.npmjs.org/:_authToken={token}\nregistry=https://registry.npmjs.org/\n",
encoding="ascii",
)
return temp_dir, npmrc
def read_package_version(repo_root: Path) -> str:
package_json = repo_root / "package.json"
content = package_json.read_text(encoding="utf-8")
marker = '"version": "'
start = content.find(marker)
if start == -1:
raise RuntimeError(f"Could not find version in {package_json}")
start += len(marker)
end = content.find('"', start)
if end == -1:
raise RuntimeError(f"Could not parse version in {package_json}")
return content[start:end]
def publish_package(repo_root: Path, token: str, skip_whoami: bool) -> str:
npm = resolve_npm()
version = read_package_version(repo_root)
temp_dir, npmrc = build_temp_npmrc(token)
env = os.environ.copy()
env["NPM_CONFIG_USERCONFIG"] = str(npmrc)
try:
if not skip_whoami:
run([npm, "whoami"], cwd=repo_root, env=env)
run(
[npm, "publish", "--access", "public", "--ignore-scripts"],
cwd=repo_root,
env=env,
)
run([npm, "view", PACKAGE_NAME, "version"], cwd=repo_root, env=env)
finally:
shutil.rmtree(temp_dir, ignore_errors=True)
return version
def update_directory(target: Path, version: str) -> None:
if not target.exists():
raise RuntimeError(f"Target directory does not exist: {target}")
npm = resolve_npm()
run([npm, "install", f"{PACKAGE_NAME}@{version}"], cwd=target)
def update_docker(service: str, version: str, compose_file: Path | None, project_dir: Path) -> None:
docker = shutil.which("docker")
if docker is None:
raise RuntimeError("Docker is not available in PATH")
command = [docker, "compose"]
if compose_file is not None:
command.extend(["-f", str(compose_file)])
command.extend(["exec", "-T", service, "npm", "install", f"{PACKAGE_NAME}@{version}"])
run(command, cwd=project_dir)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Publish the Mega n8n package and update a target n8n installation.",
)
parser.add_argument(
"--token",
default=os.environ.get("NPM_TOKEN", "").strip(),
help="Granular npm token with publish permission. Defaults to NPM_TOKEN.",
)
parser.add_argument(
"--skip-publish",
action="store_true",
help="Skip npm publish and only run the update step.",
)
parser.add_argument(
"--version",
default="",
help="Version to install on the target. Defaults to package.json version.",
)
parser.add_argument(
"--skip-whoami",
action="store_true",
help="Skip npm whoami before publish.",
)
parser.add_argument(
"--target",
default="",
help="Directory where npm install should run for the n8n update step.",
)
parser.add_argument(
"--docker-service",
default="",
help="Docker Compose service name to update with npm install inside the container.",
)
parser.add_argument(
"--docker-compose-file",
default="",
help="Optional docker-compose file to use with --docker-service.",
)
parser.add_argument(
"--docker-project-dir",
default="",
help="Working directory for docker compose. Defaults to the compose file directory or current directory.",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
repo_root = Path(__file__).resolve().parent.parent
version = args.version.strip() or read_package_version(repo_root)
if not args.skip_publish:
token = args.token
if not token:
token = getpass.getpass("NPM token: ").strip()
if not token:
print("Missing npm token. Pass --token, set NPM_TOKEN, or enter it when prompted.", file=sys.stderr)
return 2
version = publish_package(repo_root, token, args.skip_whoami)
if args.target.strip():
update_directory(Path(args.target).resolve(), version)
if args.docker_service.strip():
compose_file = Path(args.docker_compose_file).resolve() if args.docker_compose_file.strip() else None
if args.docker_project_dir.strip():
project_dir = Path(args.docker_project_dir).resolve()
elif compose_file is not None:
project_dir = compose_file.parent
else:
project_dir = Path.cwd()
update_docker(args.docker_service.strip(), version, compose_file, project_dir)
if not args.target.strip() and not args.docker_service.strip():
print("Publish completed. No update target was provided.")
else:
print(f"Completed publish/update flow for {PACKAGE_NAME}@{version}")
return 0
if __name__ == "__main__":
raise SystemExit(main())