Class CloudForgeSynthesizer

java.lang.Object
com.cloudforgeci.api.deploy.CloudForgeSynthesizer

public final class CloudForgeSynthesizer extends Object
In-process CDK synthesis — produces the canonical CloudFormation template CloudForgeDeployment (specifically AwsDirectDeployer, but the shape is target- agnostic) needs, without shelling out to the cdk CLI.

Follows the same recipe as cfc-testing's InteractiveDeployer "synthesize only" / "export template" options: build a CDK App with cfc context set from DeploymentConfig.toContextMap(), construct the com.cloudforgeci.api.launch stack for DeploymentConfig.runtime, and call app.synth(). It lives here so that callers without an interactive CLI, such as CloudForge Manager, need not depend on the sample project.

Account resolution: DeploymentConfig.account wins when set; otherwise CDK_DEFAULT_ACCOUNT; otherwise the template's Environment is account-agnostic and CloudFormation resolves AWS::AccountId in whichever account it is deployed to.

Availability-zone resolution: VpcFactory.maxAzs(2) reads Stack.availabilityZones, which CDK resolves via Fn::GetAZs (a CloudFormation-time token) only while account+region are both unresolved — true for the account-agnostic template above. The moment an explicit account is set, CDK instead routes AZ resolution through its availability-zones *synth-time* context provider — and since this deploy path never shells out to the cdk CLI to satisfy that lookup, CDK would silently return its built-in dummy values (dummy1a/dummy1b) baked straight into the template, which then fails at CloudFormation. Whenever an account is pinned, synthesize(DeploymentConfig, Path) seeds that context key itself first — see seedAvailabilityZoneContext(App, String, String, String[]).

Hosted-zone resolution: the same exposure, a different context provider — DomainFactory's HostedZone.fromLookup for an SSL+custom-domain deploy has no CloudFormation-intrinsic fallback the way AZs do, so it always resolves through CDK's synth-time hosted-zone context provider once a domain is set, and would otherwise silently bake in CDK's dummy zone id. See seedHostedZoneContext(App, String, String, String).

  • Method Details

    • synthesize

      public static CloudForgeSynthesizer.Result synthesize(DeploymentConfig config, Path outputDirectory) throws IOException
      Resolves config.applicationSpec via ApplicationLoader when not already set (mutates config — matches CloudForgeDeployment.deploy()'s existing convention of resolving/preparing the config it's handed rather than requiring the caller to have already done so).
      Throws:
      IllegalArgumentException - when applicationId/stackName/runtime are missing, or no ApplicationSpec is registered for config.applicationId
      IOException - when app.synth() fails, or the output directory can't be created
    • synthesizeForMarketplace

      public static CloudForgeSynthesizer.Result synthesizeForMarketplace(DeploymentConfig config, Path outputDirectory) throws IOException
      Same as synthesize(DeploymentConfig, Path), but produces the AWS Marketplace CloudFormation listing variant of CloudForge Manager -- see MarketplaceParameterSupport's own javadoc for what that changes (real AdminEmail/LicenseKey CloudFormation parameters instead of requiring them via --context/cdk.json, meaningless to a buyer who never runs the CDK CLI). This is the one and only place Marketplace mode is ever turned on -- deliberately not a DeploymentConfig field or a CLI flag on the regular deploy command, so it can't be set by hand-editing a context file; only whatever builds the actual Marketplace listing artifact calls this method.
      Throws:
      IllegalArgumentException - when config.applicationId isn't cloudforge-manager -- every other application ignores Marketplace mode entirely (see MarketplaceParameterSupport), so synthesizing one through this entry point would silently produce a template indistinguishable from synthesize(DeploymentConfig, Path)'s own output.
      IOException
    • synthesizeForMarketplace

      public static CloudForgeSynthesizer.Result synthesizeForMarketplace(DeploymentConfig config, Path outputDirectory, MarketplaceVariant variant) throws IOException
      Same as synthesizeForMarketplace(DeploymentConfig, Path), for an explicit MarketplaceVariant: SSO (Cognito, admin email and domain prefix required) or TOKEN (no Cognito, first administrator created with the one-time setup token).
      Throws:
      IOException
    • synthesizeAdvisoryDryRun

      public static CloudForgeSynthesizer.ComplianceCheckResult synthesizeAdvisoryDryRun(DeploymentConfig config, Path outputDirectory)
      A synth-only "what would this report" pass — never deploys anything, never writes to CloudFormation, and never throws over a compliance finding the way synthesize(DeploymentConfig, Path) can in ComplianceMode.ENFORCE. Built for an on-demand compliance check against an already-deployed stack's reconstructed configuration (CloudForge Manager's Compliance tab): the caller sets config.complianceFrameworks to whichever framework(s) the user picked, and this returns every cdk-nag and framework-rule finding for them, instead of only the subset that would block a real deploy.

      config.complianceMode is forced to ADVISORY for the duration of this call (and restored after) regardless of what the caller set it to — this method's entire point is "report, don't block". Framework-rule findings only reach a caller at all via ComplianceFindingsCollector — see its own javadoc for why Node.addValidation itself can't carry them out of ADVISORY mode.

      config.securityProfile and config.auditManagerEnabled are likewise forced (to PRODUCTION and true, both restored after) — SecurityRules#install's own gates only ever apply cdk-nag for a PRODUCTION profile and only ever install any FrameworkRules implementation at all when auditManagerEnabled is set, regardless of which frameworks were requested. Without forcing both, a check against a real DEV/STAGING stack's own reconstructed profile would silently come back with zero findings from either engine — not "this stack is compliant", just "these checks never ran" — which would actively mislead the very user this preview exists for. The whole point of an advisory check is "would this pass production-grade scrutiny", independent of whatever the target stack's own real profile happens to be.

      Never throws — not every validator in this codebase honors ComplianceMode (several "always load" cross-framework validators hard-block on their own SecurityProfile gate regardless of mode; see ComplianceCheckResult#error's own javadoc). A caller building a UI around this (CloudForge Manager's Compliance tab) can always render SOMETHING rather than handling a thrown exception on top of the findings lists it otherwise expects.

      Parameters:
      outputDirectory - a throwaway cloud-assembly directory — the caller owns its lifecycle (create before, delete after); nothing here is meant to persist