String substitution in angular-cli built distributions

the Angular CLI tool makes building distribution packages for Angular 2 spas a snap. The resulting dist package is versioned, compressed, minimized.

However, is this a problem for Octopus Deploy? I’ve tried to make a step that sets the backend api url; i think i’m running into a couple of problems though:
a) the string substitution wants a file name in which to look for the variables to be expanded; however, the cli renames file on the fly to prevent caching problems on re-distribution.

b) for grins, i made a distribution package and set the file to be examined to be the temp name that the cli assigned… ‘main.831e0cff932942b94962.bundle.js’; however, the cli also compresses all the bundles, so the variable has already been squirreled away into a .gz file

Do you have any suggestions on how to work these issues?

thanks in advance

Okay, so i did some experimentation.

Turns out that ng build doesn’t touch anything in the includes folder. So, i created a ‘my_globals.ts’ file in the includes folder, containing an exported constant array of global variables.

like this:

export const my_global = {
production: false,
apiRoot: ‘#{COEStudentAPIURL}’
};

Then, i used the 'substitute variables in files" feature, and tried to get Octopus to set the correct apiRoot value for the corresponding environment.

The raw/verbose log shows that COEStudentAPIURL was evaluated correctly, however, the value in the file was not changed…

Now, in fact, the first time i tried to deploy, i had put in the wrong path, and got an IO error. So, i changed the file path, and Octopus didn’t complain, so i’m pretty sure i’m hitting the right file.

Seems like that should have worked… what gives? Is there some sort of pipeline documented somewhere that explains what variables are available when?

thanks

ServerTasks-212.log.txt (191 KB)

Hi,

What you are doing in your second post is exactly how I handle this (well, I usually have a config.json file that I parse, but essentially the same thing) and this absolutely should have worked, and in my testing, trying to set up a package with the same file/folder structure it worked well.

My only suggestion is are you checking the correct file? Your substituted file will be in d:\Octopus\Applications\HOCLocal\COEStudentAPI_WebUI_HOCLocal\1.0.56_2\COEStudentAPI_WebUI_HOCLocal\include\my_global.ts Note the version folder 1.0.56_2, your previous failed deployment attempts will be in 1.0.56 and 1.0.56_1, we create a new folder for each deployment. It’s easy to miss if you’re deploying and refreshing a file.

Failing that, is that the whole content of your my_globals.ts file? If not can you share the whole file?

Regards,
Mark

thanks for your reply.

Knowing that you were able to get it to work, first tried using the octopus environment variable associated with the install directory.

that didn’t work very well (first and second screen shots)

THen I re read the path requirement on that configuration option, and realized that what i really wanted was a path relative to the install directory. After i did that, things are working fine.

thanks again, and i’ll now close the discussion

i guess i closed this discussion a little too soon.
I don’t know that this is an ‘octopus’ problem; it might be the way the cli bundles things together
but you mentioned you did exactly this to configure a web ui, except that you used a json file; so i thought I’d ask if you could shed some light on my situation.

I changed my_global_environment.ts to be globals.json, and stuffed a json object in it representing the ‘apiRoot’, and a version string.

looks like this:
{
“apiRootFromJson”:"#{COEStudentAPIURL}",
“versionString”: “#{Octopus.Release.Number}”
}

COEStudentAPIURL is an octopus variable in the project, scoped to the different environments, and of course Octopus.Release.Number is the system variable.

I deploy to the environments, and, looking at globals.json in the installed include folder, i can see that the variables are evaluated… All is well and good, and octopus has done it’s job.

The problem is, i can’t seem to get to the variable from the cli-generated distributed files.

From inside one of the modules of the web ui, the code looks like:

var globals = require(’…/include/globals.json’);

@Injectable()
export class AppConfigService {

constructor() {

...
this.ROOTURL = globals.apiRootFromJson;//environment.apiRoot
console.log(this.ROOTURL)
...

}

}

and the log displays: COEStudentAPIURL!! :frowning:

Like i say, it doesn’t seem to be an octopus problem, but it still leaves open the question of… ‘how do i use octopus to configure SPA deployments’… so, if you can’t answer this specific issue, but can help me with what i’m trying to accomplish, i’d really appreciate it.

thanks for looking at this.

Hi,

I don’t know much about Angular-CLI, my theory is that webpack (which is used by Angular-CLI) is automatically including the globals.json file into the bundle since you are requiring it, so you end up doing Octopus substitution on the external file, but the code reads the bundled file. In raw webpack you can exclude something from the module loaders (see https://webpack.github.io/docs/configuration.html#module-loaders) but I’m afraid I don’t know how you do that with angular-cli, you might need to dig through their documentation and see if it’s possible.

Mark

thanks mark;

the require must be it.

hi mark; in case anyone else asks about this question (seems like lots of
people would want to do this)
here is the answer:

There is no issue with substituing varialables in the Angular-cli.

  1. Using the environment.[environment].ts setup you may have the following:
    environment.ts (local development)
    environment.staging.ts (octopus staging environment)
    environment.production.ts (octopus live environment)

Inside staging and production environments you will have something like this:
export const environment = {
production: true,
mandrillApiKey: ‘#{mandrillApiKey}’,
auth0ClientId: ‘#{auth0ClientId}’,
auth0SubDomain: ‘#{auth0SubDomain}’,
portalUrl: “#{portalUrl}”,
inboundEmail: ‘#{inboundEmail}’
};

  1. Building your application locally and searching the dist folder for one of the octopus variables: #{mandrillApiKey} shows that the file main.[some random string].bundle.js is where the environment variables get compiled to.

  2. In your octopus deploy process step configure the “Substitute variables in files” feature and within the “Target files” textarea enter main.*.bundle.js

In short to answers your question, octopus supports wildcards with the star (*) so the CLI’s random string filename doesn’t inhibit us.

I would think the correct way to solve the op’s use case is to use the environment.ts file as mentioned by Nicholas. Any substitution should then be done by the build/CI server. ng-build produces the compiled/transpiled /dist folder as an artefact which should then be packaged and deployed like any other project - would it be Octopus responsibility to modify this directory after build? Wouldn’t it be like trying to modify a dll in a nuget package at deployment time?

It is absolutely the release management’s job to modify this file with environment specific variables at deploy time, and is what makes any release management system powerful.

Wouldn’t it be like trying to modify a dll in a nuget package at deployment time?

You’re comparing apple’s to orange’s. In the case of modifying the environment.ts that is like modifying variables in a web.config, not changing compiled code. Application settings are always put into some type of common settings file for the exact use case that we are discussing today; the ability to easily swap them out. I haven’t run into a use case where configuration variables were necessary in whatever I was packaging up into a nuget package, typically the hosting application would provide the necessary environment specific variables.

You shouldn’t ever be storing production credentials in source control or in a place where developers have access to them. What always ends up happening is someone can’t reproduce something easily so they temporarily use the production database connections string and introduce a lot of risk, among other reasons.

Not sure I mentioned anything about storing credentials in source control?

I guess the Angular cli already provides a facility for modifying environment specific variables using the environment file, ng-build and ng-serve take care of replacing these values in the output /dist folder. I agree with what you’re saying you shouldn’t have to package up the /dist folder as part of build per environment as it is Octopus job to know about environments. Just doesn’t feel that right to go modifying minified, transpiled and compressed files in the /dist folder

Ben,

Not sure I mentioned anything about storing credentials in source control?

No you didn’t, but I didn’t think you fully understood enterprise deployments and release management so I expanded upon best practices.

Sure, Angular CLI and web.config transforms work, but they are designed to get you up and running quickly and they transform environment configs at build time instead of at release time. That is ultimately inflexible because it’s unnecessary to rebuild the app each time for each environment.

yeah not sure why the angular-cli would behave this way, would have thought the developers working on that project fully understood enterprise deployments and release management.

Maybe they don’t understand the awesomeness of Octopus Deploy!

Hi, I am adopting this approach which works well. However the transform step when altering the main.xxx.bundle file changes Javascript variables I was not expecting to see changed.E.g., code like “return e[“ɵmod”]([e[“ɵmpd”]” becomes “return e[”?mod"]([e["?mpd"]", where the ‘e’ is replaced with ‘?’. I’m new to using Octopus and cant understand how or where the transform is occurring. Any ideas? Thanks