abaga129 / sveltekit-adapter-iis

15 stars 5 forks source link

Support for multiple transformation files web.{stage}.config based on .env files #5

Closed Palkess closed 10 months ago

Palkess commented 10 months ago

In one of my projects where I'm using this adapter I have several staging environments: local, dev, test, pre-prod, prod. Currently when deploying via Azure Pipelines I apply changes to environment variables in \<appSettings> through XML variable substitution so I set up corresponding variables with matching names on each stage release.

Maybe this is correct and should be the way to do stage specific settings but in my other .NET projects we create stage specific transformation files, like web.prod.config, that lives next to the default web.config in the repos making the source of truth closer to the code base instead of separating them in the CI/CD flows.

There's a few different solutions to this that could be implemented but I think checking for stage specific .env files and creating transformation files based on the variables would be a good way to do it.

Example of I would imagine it could work:

Assume I have the following .env files:

.env

DATABASE_CONNECTIONSTRING=local-connection
SMTP_HOST=local-smtp-host

.env.prod

DATABASE_CONNECTIONSTRING=prod-connection
SMTP_HOST=prod-smtp-host

After building the project the following .config files would be created:

web.config

...
<appSettings>
    ...
    <add key="DATABASE_CONNECTIONSTRING" value="local-connection" />
    <add key="SMTP_HOST" value="local-smtp-host" />
</appSettings>
...

web.prod.config

...
<appSettings>
...
<add key="DATABASE_CONNECTIONSTRING" value="prod-connection" xdt:Transform="SetAttributes(value)" xdt:Locator="Match(key)" />
<add key="SMTP_HOST" value="prod-smtp-host" xdt:Transform="SetAttributes(value)" xdt:Locator="Match(key)" />
</appSettings>
...

Would a feature like this be a good addition to the project or should stage specific .env variables be handled in a different way?

KraXen72 commented 10 months ago

does IIS consider anything else than a web.config file? like, would it even look at a web.prod.config file? it is also possible i might not understand your usecase fully.

Palkess commented 10 months ago

No, when the site is up and running in IIS it only cares about web.config. The transformation usually happens during the deployment of the built package, in my case when I trigger my releases in Azure Devops.

I'll try to give some context by sharing some screenshots:

This is how my release looks like in Azure Devops

Screenshot 2024-01-25 091021

And here are the steps it takes to deploy my artifact. During my deploy-step I can choose to transform my *.config-files a few different ways, from what I've seen working with other .NET projects running XML Transformations are the most common. So after I've run my deploy, the actual web.config on my server has the prod values in the appSettings keys instead of my local values. The IIS doesn't interact at all with the transformation files.

Screenshot 2024-01-25 090609

Hopefully it made a bit more sense with some context!

KraXen72 commented 10 months ago

it wouldn't be that hard to add, i guess do you want to explicitly define the env files considered, or just do all .env* files?

Palkess commented 10 months ago

I think checking for any .env.{stage} files and writing a web.{stage}.config with transforms for each would be straight forward and follows a common pattern when defining stage specific .env/.config-files. Something I haven't considered here is if there could be any values outside of \<appSettings> that could be stage specific, I guess that's a discussion for when someone has a use-case for it.

KraXen72 commented 10 months ago

i'm working on a pr

KraXen72 commented 10 months ago

image @Palkess

KraXen72 commented 10 months ago

if you want to, add documentation for this, because i forgot plus you can probably better explain why it's useful

Palkess commented 10 months ago

Excellent, that was very fast! 😄 There's a few more things that's needed, the transformation web.{stage}.config should only contain the key/values that is going to change and has some more attributes on \<configuration> and \ that are needed so some changes are needed in web.config.js. It's described in https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/transform-webconfig?view=aspnetcore-8.0#build-configuration

I'm currently at work, I can write some documentation on it later.

KraXen72 commented 10 months ago

in the link you sent, it looks like it's using <environmentVariables>, but this adapter uses <appSettings> to pass env vars to IISnode, so which one snould be used? can you send a full xml transformation file for sveltekit&iis? i'll adjust it accordingly

Palkess commented 10 months ago

@KraXen72 I'm sorry, I gave a bad example in a rush. From my example above, here's what the complete web.prod.config-file would look like in that scenario:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="https://schemas.microsoft.com/XML-Document-Transform">
  <appSettings>
    <add key="DATABASE_CONNECTIONSTRING" value="prod-connection" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
    <add key="SMTP_HOST" value="prod-smtp-host" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
  </appSettings>
</configuration>

So the xdt:Locator attribute matches the element based on the key value and then the xdt:Transform is set to replaces all attributes on that element, in this case value.

Here's a link to an online XML transform tester, it's a bit sensitive to tabs between attributes though in case you run in to any trouble with it. https://elmah.io/tools/webconfig-transformation-tester/

KraXen72 commented 10 months ago

i see. thanks for the example. i am now thinking if it would be possible to have staging like this even outside of .NET/AWS environments, because i too usually do 2 build types.

while the XML transforms are a good thing to implement and i'll continue my work on the pr, i'm open to ideas on how this could be achieved, for example with cli flags for using different ENV variables & setting passed to the adapter.
it's getting quite annoying changing it by hand every time, and i don't use an AWS platform for deplyment, i just copy the build over ftp :P

abaga129 commented 10 months ago

How I think this could be implemented fairly easily would be

import { vitePreprocess } from '@sveltejs/kit/vite'
import IISAdapter from 'sveltekit-adapter-iis'

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: vitePreprocess(),

  kit: {
    version: {
      pollInterval: 300000,
    },
    adapter: IISAdapter({
      // the hostname/port that the site will be hosted on in IIS.
      // can be changed later in web.config
      origin: 'http://localhost:80XX',
      // ... other options
      transforms: [
          {
             environment: 'production',
             origin: 'https://productionurl.com'
          },
           {
             environment: 'staging',
             origin: 'https://stagingurl.com'
          }
      ]
    }),
  },
}

export default config

The transforms property would be an array of type AdapterOptions where we just ignore the transforms property on them (since we cant have nested transforms). Then that would enable us to just reuse the same bit of code to generate each web.*.config file. What are your thoughts?

KraXen72 commented 10 months ago

not necessarily my usecase. probably confusing of me that i brought it up in this issue, but for my usecase i don't care about creating multiple web.config files, just a different one preferrably based on a cli flag, that would select different adapter options (so i can create npm scripts like build:prod, build:test etc.) i'll try finishing up this xml transform pr sooner or later and we can take a look at this in a different issue/pr.

KraXen72 commented 10 months ago

i finished up the pr

abaga129 commented 10 months ago

This has been merged and published as v1.2.0