@wtfalch/foundry 0.26.2 → 0.26.3

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.
@@ -128,6 +128,13 @@ export declare const AGORA_APP_PORTS_EXPOSES = "8080";
128
128
  * so Agora is never given an address that does not resolve yet.
129
129
  */
130
130
  export declare const CALLS_SERVICE_NAME_SCOPE_KEY = "coolify_calls_service_internal_name";
131
+ /**
132
+ * Recorded by the executor once the calls service has been deployed and
133
+ * healthy. `createGithubAppApplication` never deploys a new app, so without
134
+ * it `planCallsService` finds every marker and plans nothing (same shape as
135
+ * {@link AGORA_DEPLOYED_SCOPE_KEY}).
136
+ */
137
+ export declare const CALLS_SERVICE_DEPLOYED_SCOPE_KEY = "coolify_calls_service_deployed";
131
138
  /**
132
139
  * The calls service app's fixed internal name: the executor gives the app
133
140
  * this `custom_internal_name`, so Agora reaches it as
@@ -136,6 +136,13 @@ export const AGORA_APP_PORTS_EXPOSES = '8080';
136
136
  * so Agora is never given an address that does not resolve yet.
137
137
  */
138
138
  export const CALLS_SERVICE_NAME_SCOPE_KEY = 'coolify_calls_service_internal_name';
139
+ /**
140
+ * Recorded by the executor once the calls service has been deployed and
141
+ * healthy. `createGithubAppApplication` never deploys a new app, so without
142
+ * it `planCallsService` finds every marker and plans nothing (same shape as
143
+ * {@link AGORA_DEPLOYED_SCOPE_KEY}).
144
+ */
145
+ export const CALLS_SERVICE_DEPLOYED_SCOPE_KEY = 'coolify_calls_service_deployed';
139
146
  /**
140
147
  * The calls service app's fixed internal name: the executor gives the app
141
148
  * this `custom_internal_name`, so Agora reaches it as
package/dist/plan/diff.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { VERSIONS, floatingTagBlocked } from '../bootstrap/versions.js';
2
2
  import { APP_ENTRY_KEY, ownerMailboxRecorded, ownerMailboxes } from '../org.js';
3
- import { AGORA_ACTIVATED_ENV_SCOPE_KEY, AGORA_APP_PORTS_EXPOSES, CALLS_INTROSPECTION_SCOPE_KEY, CALLS_INTROSPECTION_UNDELIVERED_SCOPE_KEY, CHAT_INTROSPECTION_SCOPE_KEY, NO_AGORA_HOST_VALUES, ZITADEL_ORG_SCOPE_KEY, agoraEnvDrift, agoraEnvRevision, agoraHostMissing, agoraHostnameFor, callsServiceHasName, callsServiceHasTokenEnv, callsTokenEnvMovedMessage, callsTokenHostFrom, desiredAgoraEnv, withRecordedMessagingProject, } from './agora.js';
3
+ import { AGORA_ACTIVATED_ENV_SCOPE_KEY, AGORA_APP_PORTS_EXPOSES, CALLS_INTROSPECTION_SCOPE_KEY, CALLS_INTROSPECTION_UNDELIVERED_SCOPE_KEY, CALLS_SERVICE_DEPLOYED_SCOPE_KEY, CHAT_INTROSPECTION_SCOPE_KEY, NO_AGORA_HOST_VALUES, ZITADEL_ORG_SCOPE_KEY, agoraEnvDrift, agoraEnvRevision, agoraHostMissing, agoraHostnameFor, callsServiceHasName, callsServiceHasTokenEnv, callsTokenEnvMovedMessage, callsTokenHostFrom, desiredAgoraEnv, withRecordedMessagingProject, } from './agora.js';
4
4
  import { BOULE_BROKER_ENTRY_KEY, bouleEnvDrift, desiredBouleEnv, wantedBouleBrokerEntry, } from './boule.js';
5
5
  import { brokerEntryAppliedScopeKey, brokerEntryWarningsScopeKey, entryAuthEnv, entryAuthEnvAppliedScopeKey, wantedBrokerEntry, } from './broker-entries.js';
6
6
  import { chatInternalNames } from './chat-matrix.js';
@@ -1350,11 +1350,12 @@ function planCallsService(org, byKey, claimed, callsReady, recordedIds, callsBlo
1350
1350
  const missingName = !callsServiceHasName(org, recordedIds);
1351
1351
  const missingTokenEnv = !callsServiceHasTokenEnv(tokenHost, recordedIds);
1352
1352
  const missingDbSplit = !recordedIds[serviceDbEnvAppliedScopeKey('calls-service')];
1353
+ const missingDeploy = !recordedIds[CALLS_SERVICE_DEPLOYED_SCOPE_KEY];
1353
1354
  if (org.calls.enabled &&
1354
1355
  callsReady &&
1355
1356
  !callsBlocked &&
1356
1357
  byKey.has(key) &&
1357
- (missingName || missingTokenEnv || missingDbSplit)) {
1358
+ (missingName || missingTokenEnv || missingDbSplit || missingDeploy)) {
1358
1359
  return {
1359
1360
  step: withHost({
1360
1361
  action: {
@@ -1368,7 +1369,9 @@ function planCallsService(org, byKey, claimed, callsReady, recordedIds, callsBlo
1368
1369
  ? `set the calls service's internal name for ${org.slug}`
1369
1370
  : missingTokenEnv
1370
1371
  ? `set the calls service's ZITADEL token env for ${org.slug}`
1371
- : `give the calls service's owner and runtime database URLs for ${org.slug} and redeploy it (#239, #456)`,
1372
+ : missingDbSplit
1373
+ ? `give the calls service's owner and runtime database URLs for ${org.slug} and redeploy it (#239, #456)`
1374
+ : `deploy the calls service for ${org.slug}`,
1372
1375
  destroys: false,
1373
1376
  migrates: false,
1374
1377
  },
@@ -2,7 +2,7 @@ import { randomBytes } from 'node:crypto';
2
2
  import { URL } from 'node:url';
3
3
  import { CALLS_SFU_PRIVATE_URL, CALLS_SFU_PUBLIC_URL } from '../calls-sfu.js';
4
4
  import { APP_ENTRY_KEY, appEntry, filesCollectionEnvName, ownerMailboxRecorded, ownerMailboxes, sharedSecretRef, } from '../org.js';
5
- import { AGORA_ACTIVATED_ENV_SCOPE_KEY, AGORA_APP_PORTS_EXPOSES, AGORA_CLIENT_ID_SCOPE_KEY, AGORA_DEPLOYED_SCOPE_KEY, APP_CLIENT_ID_SCOPE_KEY, CALLS_INTROSPECTION_SCOPE_KEY, CALLS_INTROSPECTION_UNDELIVERED_SCOPE_KEY, CALLS_SERVICE_NAME_SCOPE_KEY, CALLS_SERVICE_PORT, CALLS_SERVICE_TOKEN_ENV_SCOPE_KEY, CHAT_INTROSPECTION_SCOPE_KEY, CHAT_INTROSPECTION_UNDELIVERED_SCOPE_KEY, CHAT_SERVICE_URL_SCOPE_KEY, CMS_CLIENT_ID_SCOPE_KEY, ENTRY_CLIENT_ID_SCOPE_PREFIX, ZITADEL_MESSAGING_PROJECT_SCOPE_KEY, ZITADEL_ORG_SCOPE_KEY, agoraEnvDrift, agoraEnvRevision, agoraHostnameFor, callsIntrospectionAppName, callsServiceHasName, callsServiceInternalName, callsTokenEnvMarker, callsTokenEnvMovedMessage, chatIntrospectionAppName, desiredAgoraEnv, entryClientIdScopeKey, } from './agora.js';
5
+ import { AGORA_ACTIVATED_ENV_SCOPE_KEY, AGORA_APP_PORTS_EXPOSES, AGORA_CLIENT_ID_SCOPE_KEY, AGORA_DEPLOYED_SCOPE_KEY, APP_CLIENT_ID_SCOPE_KEY, CALLS_INTROSPECTION_SCOPE_KEY, CALLS_INTROSPECTION_UNDELIVERED_SCOPE_KEY, CALLS_SERVICE_DEPLOYED_SCOPE_KEY, CALLS_SERVICE_NAME_SCOPE_KEY, CALLS_SERVICE_PORT, CALLS_SERVICE_TOKEN_ENV_SCOPE_KEY, CHAT_INTROSPECTION_SCOPE_KEY, CHAT_INTROSPECTION_UNDELIVERED_SCOPE_KEY, CHAT_SERVICE_URL_SCOPE_KEY, CMS_CLIENT_ID_SCOPE_KEY, ENTRY_CLIENT_ID_SCOPE_PREFIX, ZITADEL_MESSAGING_PROJECT_SCOPE_KEY, ZITADEL_ORG_SCOPE_KEY, agoraEnvDrift, agoraEnvRevision, agoraHostnameFor, callsIntrospectionAppName, callsServiceHasName, callsServiceInternalName, callsTokenEnvMarker, callsTokenEnvMovedMessage, chatIntrospectionAppName, desiredAgoraEnv, entryClientIdScopeKey, } from './agora.js';
6
6
  import { BOULE_BROKER_ENTRY_KEY, BOULE_CLIENT_ID_SCOPE_KEY, BOULE_DATABASE_SCOPE_KEY, BOULE_DEPLOYED_SCOPE_KEY, bouleEnvDrift, bouleHostnameFor, desiredBouleEnv, wantedBouleBrokerEntry, } from './boule.js';
7
7
  import { brokerEntryAppliedScopeKey, brokerEntryWarningsScopeKey, entryAuthEnv, entryAuthEnvAppliedScopeKey, wantedBrokerEntry, writeBrokerEntry, } from './broker-entries.js';
8
8
  import { MAS_IMAGE, MAS_TAG, SYNAPSE_IMAGE, SYNAPSE_TAG, callsMasConfigYaml, callsServerName, callsSynapseHomeserverYaml, mintCallsMatrixSecrets, parseCallsDbUrl, } from './calls-matrix.js';
@@ -3811,6 +3811,7 @@ export class FoundryStepExecutor {
3811
3811
  name,
3812
3812
  domains: coolifyTunnelDomains([callsHostnameFor(org)]),
3813
3813
  dockerfile_location: CALLS_SERVICE_DOCKERFILE,
3814
+ ports_exposes: CALLS_SERVICE_PORT,
3814
3815
  }));
3815
3816
  // A fixed internal name, like Synapse and MAS above, so the org's Agora
3816
3817
  // reaches this app as `http://<name>:8080` (`agora.ts`'s `AGORA_CALLS_UPSTREAM`).
@@ -3820,7 +3821,13 @@ export class FoundryStepExecutor {
3820
3821
  // tells `desiredAgoraEnv` that the name and the env are both in place.
3821
3822
  // No Coolify health check on the calls service (#543 review): its image
3822
3823
  // is node:24-alpine with no curl, which Coolify's container check runs.
3823
- await this.ports.coolify.updateApplication(app.appId, { custom_internal_name: name });
3824
+ // A found app may predate `ports_exposes` on create; patch a differing one.
3825
+ const portDiffers = app.existed &&
3826
+ (await this.ports.coolify.getApplication(app.appId)).ports_exposes !== CALLS_SERVICE_PORT;
3827
+ await this.ports.coolify.updateApplication(app.appId, {
3828
+ custom_internal_name: name,
3829
+ ...(portDiffers ? { ports_exposes: CALLS_SERVICE_PORT } : {}),
3830
+ });
3824
3831
  // Every app foundry deploys keeps its own logs (#75), reused as-is here.
3825
3832
  await this.ensureLogVolume(app.appId, org, app.existed);
3826
3833
  const bindingSecret = await this.ensureCallsBindingSecret(app.appId);
@@ -3886,12 +3893,18 @@ export class FoundryStepExecutor {
3886
3893
  // here would bring back a container built before that migrate step
3887
3894
  // existed, still pointed at a role nothing has created.
3888
3895
  const givesTokenEnv = token !== null && context.recordedIds()[CALLS_SERVICE_TOKEN_ENV_SCOPE_KEY] !== tokenMarker;
3889
- if (app.existed && dbSplitNeedsRefresh) {
3896
+ // A new app is never deployed by Coolify (`instant_deploy` defaults to
3897
+ // false), so a missing deployed marker or a port fix deploys too. One
3898
+ // deploy covers all three cases and the restart below never follows it.
3899
+ if ((app.existed && dbSplitNeedsRefresh) ||
3900
+ !context.recordedIds()[CALLS_SERVICE_DEPLOYED_SCOPE_KEY] ||
3901
+ portDiffers) {
3890
3902
  // #543: every deploy waits for health -- the calls service's own
3891
3903
  // public hostname and `/healthz`, the same path the quartet above
3892
3904
  // just set on the app.
3893
3905
  const runtime = this.ports.runtime ?? {};
3894
3906
  await waitForNewDeployment(this.ports.coolify, app.appId, publicHealthCheck(org, context.recordedIds(), callsHostnameFor(org), CALLS_HEALTH_CHECK_PATH), runtime, runtime.sleep ?? defaultSleep, 'coolify:calls-service', `${org.slug}'s calls service stays at its previous deploy`);
3907
+ context.recordScopeId(CALLS_SERVICE_DEPLOYED_SCOPE_KEY, '1');
3895
3908
  }
3896
3909
  else if (app.existed && (!callsServiceHasName(org, context.recordedIds()) || givesTokenEnv)) {
3897
3910
  await this.ports.coolify.restart(app.appId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wtfalch/foundry",
3
- "version": "0.26.2",
3
+ "version": "0.26.3",
4
4
  "description": "Foundry's SDK: the declared org's types, the plan and apply contracts, and the state store's interface.",
5
5
  "repository": {
6
6
  "type": "git",