Batched env var update

X-Lovable-Edit-ID: edt-05d80315-4508-4857-999d-b3f2d9b8102d
Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot] 2026-04-27 15:32:35 +00:00
commit f4736592ee
3 changed files with 23 additions and 3 deletions

View file

@ -80,7 +80,6 @@ export async function configureRailwayService(opts: {
variables: Record<string, string>; variables: Record<string, string>;
startCommand?: string; startCommand?: string;
}): Promise<void> { }): Promise<void> {
// O Railway expõe variáveis via variableUpsert (uma por vez) e fonte/imagem via serviceInstanceUpdate.
// Setamos a imagem (e startCommand opcional) primeiro. // Setamos a imagem (e startCommand opcional) primeiro.
const updateSource = ` const updateSource = `
mutation ServiceInstanceUpdate($serviceId: String!, $environmentId: String!, $input: ServiceInstanceUpdateInput!) { mutation ServiceInstanceUpdate($serviceId: String!, $environmentId: String!, $input: ServiceInstanceUpdateInput!) {
@ -104,7 +103,20 @@ export async function configureRailwayService(opts: {
throw new Error(`serviceInstanceUpdate (source) failed: ${JSON.stringify(sourceRes.errors)}`); throw new Error(`serviceInstanceUpdate (source) failed: ${JSON.stringify(sourceRes.errors)}`);
} }
// Agora as variáveis. Railway recomenda variableUpsert por chave. // Agora as variáveis. Use batch + skipDeploys para evitar um deploy/rate-limit por env var.
if (opts.projectId) {
await upsertRailwayVariableCollection({
token: opts.token,
serviceId: opts.serviceId,
environmentId: opts.environmentId,
projectId: opts.projectId,
variables: opts.variables,
skipDeploys: true,
});
return;
}
// Fallback legado caso algum caller antigo não tenha projectId.
for (const [name, value] of Object.entries(opts.variables)) { for (const [name, value] of Object.entries(opts.variables)) {
await upsertRailwayVariable({ await upsertRailwayVariable({
token: opts.token, token: opts.token,
@ -113,6 +125,7 @@ export async function configureRailwayService(opts: {
projectId: opts.projectId, projectId: opts.projectId,
name, name,
value, value,
skipDeploys: true,
}); });
} }
} }
@ -148,19 +161,21 @@ export async function upsertRailwayVariableCollection(opts: {
projectId: string; projectId: string;
variables: Record<string, string>; variables: Record<string, string>;
replace?: boolean; replace?: boolean;
skipDeploys?: boolean;
}): Promise<void> { }): Promise<void> {
const mutation = ` const mutation = `
mutation VariableCollectionUpsert($input: VariableCollectionUpsertInput!) { mutation VariableCollectionUpsert($input: VariableCollectionUpsertInput!) {
variableCollectionUpsert(input: $input) variableCollectionUpsert(input: $input)
} }
`; `;
const input = { const input: Record<string, unknown> = {
projectId: opts.projectId, projectId: opts.projectId,
environmentId: opts.environmentId, environmentId: opts.environmentId,
serviceId: opts.serviceId, serviceId: opts.serviceId,
variables: opts.variables, variables: opts.variables,
replace: opts.replace ?? false, replace: opts.replace ?? false,
}; };
if (opts.skipDeploys !== undefined) input.skipDeploys = opts.skipDeploys;
const res = await railwayQuery(mutation, { input }, opts.token); const res = await railwayQuery(mutation, { input }, opts.token);
if (res.errors?.length) { if (res.errors?.length) {
throw new Error(`variableCollectionUpsert failed: ${JSON.stringify(res.errors)}`); throw new Error(`variableCollectionUpsert failed: ${JSON.stringify(res.errors)}`);
@ -178,6 +193,7 @@ export async function upsertRailwayVariable(opts: {
projectId?: string; projectId?: string;
name: string; name: string;
value: string; value: string;
skipDeploys?: boolean;
}): Promise<void> { }): Promise<void> {
const mutation = ` const mutation = `
mutation VariableUpsert($input: VariableUpsertInput!) { mutation VariableUpsert($input: VariableUpsertInput!) {
@ -191,6 +207,7 @@ export async function upsertRailwayVariable(opts: {
value: opts.value, value: opts.value,
}; };
if (opts.projectId) input.projectId = opts.projectId; if (opts.projectId) input.projectId = opts.projectId;
if (opts.skipDeploys !== undefined) input.skipDeploys = opts.skipDeploys;
const res = await railwayQuery(mutation, { input }, opts.token); const res = await railwayQuery(mutation, { input }, opts.token);
if (res.errors?.length) { if (res.errors?.length) {
@ -220,6 +237,7 @@ export async function setHermesSuspended(opts: {
projectId: opts.projectId, projectId: opts.projectId,
name: "HERMES_SUSPENDED", name: "HERMES_SUSPENDED",
value: opts.suspend ? "true" : "", value: opts.suspend ? "true" : "",
skipDeploys: true,
}); });
await deployRailwayService({ await deployRailwayService({

View file

@ -487,6 +487,7 @@ async function handleUpdateExistingService(
environmentId, environmentId,
projectId, projectId,
variables, variables,
skipDeploys: true,
}); });
console.log(`[provision-agent:update] variáveis atualizadas (${Object.keys(variables).length})`); console.log(`[provision-agent:update] variáveis atualizadas (${Object.keys(variables).length})`);

View file

@ -115,6 +115,7 @@ Deno.serve(async (req) => {
environmentId, environmentId,
projectId, projectId,
variables, variables,
skipDeploys: true,
}); });
await deployRailwayService({ await deployRailwayService({