@halospv3/hce.shared-config
Version:
Automate commit message quality, changelogs, and CI/CD releases. Exports a semantic-release shareable configuration deserialized from this package's '.releaserc.yml'. Shared resources for .NET projects are also distributed with this package.
185 lines (169 loc) • 6.91 kB
text/typescript
import { ok } from 'node:assert/strict';
import { getEnvVarValue } from '../envUtils.js';
import { MSBuildProject, MSBuildProjectPreDefinedProperties } from './MSBuildProject.js';
import { getGithubNugetRegistryPair, nugetGitHubUrlBase } from './dotnetGHPR.js';
import { getGitlabNugetRegistryPair } from './dotnetGLPR.js';
function formatDotnetPublish(projectsToPublish: string[], publishProperties: string[]): string {
/* Fun Fact: You can define a property and get the evaluated value in the same command!
```pwsh
dotnet msbuild .\src\HXE.csproj -property:RuntimeIdentifiers="""place;holder""" -getProperty:RuntimeIdentifiers
place;holder
```
enclosing with """ is required in pwsh to prevent the semicolon from breaking the string.
*/
if (!(Array.isArray(projectsToPublish) && projectsToPublish.length > 0))
throw new Error(`Type of projectsToPublish (${typeof projectsToPublish}) is not allowed. Expected a string[] where length > 0.`);
// each may have TargetFramework OR TargetFrameworks (plural)
const evaluatedProjects: MSBuildProject[] = projectsToPublish.map(
(proj) => new MSBuildProject(proj, publishProperties),
);
// args appended to "dotnet publish", joined by space
return evaluatedProjects.flatMap(proj => {
const args: string[] = [proj.Properties.FullPath];
function appendCustomProperties(publishProperties: string[]) {
// convert to dictionary and filter for user-defined properties.
const dictionary = Object.entries(proj.Properties).filter(
(p) => !publishProperties.includes(p[0]),
);
if (dictionary.length > 0) {
/* format remaining properties as "-p:Property=Value" and append to args */
args.push(
...dictionary.map((keyValuePair) => `-p:${keyValuePair[0]}=${keyValuePair[1]}`),
);
}
}
appendCustomProperties(publishProperties);
const cmdPermutations: string[][] = []; // forEach, run dotnet [...args,...v]
function formatFrameworksAndRuntimes() {
const RIDs: string[] =
proj.Properties.RuntimeIdentifiers.length > 0
? proj.Properties.RuntimeIdentifiers.split(';')
: [];
const TFMs: string[] =
proj.Properties.TargetFrameworks.length > 0
? proj.Properties.TargetFrameworks.split(';')
: [];
if (RIDs.length > 0) {
if (TFMs.length > 0) {
for (const RID of RIDs) {
for (const TFM of TFMs) {
cmdPermutations.push(['--runtime', RID, '--framework', TFM]);
}
}
} else {
// assume singular TFM. No need to specify it.
for (const RID of RIDs) {
cmdPermutations.push(['--runtime', RID]);
}
}
} else if (TFMs.length > 0) {
for (const TFM of TFMs) {
cmdPermutations.push(['--framework', TFM]);
}
}
}
formatFrameworksAndRuntimes();
return cmdPermutations.length > 0
? cmdPermutations.map((permArgs) => [...args, ...permArgs]) // string[][]
: [args]; // string[][]
}).map((args) => `dotnet publish ${args.join(' ')}`).join(' && ');
}
function formatDotnetPack(projectsToPackAndPush: string[] | false): string {
return projectsToPackAndPush === false
? ""
: projectsToPackAndPush
.map(v => `dotnet pack ${v}`)
.join(' && ');
}
function formatDotnetNugetSign(dotnetNugetSignArgs: string[]): string {
switch (dotnetNugetSignArgs.length) {
case 0:
return '';
default:
return `dotnet nuget sign ${dotnetNugetSignArgs.join(' ')}`;
// default: {
// throw new Error("")
// // this needs a rework.
// const packagePaths: string[] = [];
// dotnetNugetSignArgs.forEach((dotnetNugetSignArg, i) => {
// // if current arg doesn't start with '-' and (current arg is first -OR- previous arg also does not start with '-')...
// if (!dotnetNugetSignArg.startsWith("-") && (i === 0 || (i > 0 && !dotnetNugetSignArgs[i - 1].startsWith("-")))) {
// // ...then it's probably a package path.
// packagePaths.push(dotnetNugetSignArg);
// }
// });
// if (packagePaths.length === 0)
// return `dotnet nuget sign ${dotnetNugetSignArgs.join(" ")}`
// else return
// }
}
}
/**
* Build a prepareCmd string from .NET project paths and `dotnet nuget sign` arguments.
*
* todo: parse Solution files to publish all projects with default Publish parameters (as evaluated by MSBuild). If multi-targeting frameworks and/or runtime, evaluate those properties for Publish permutation matrix.
* todo: cleanup, docs
* @export
* @param {string[]} projectsToPublish
* @param {string[]|false} projectsToPackAndPush Relative and/or full file paths of projects to pass to `dotnet pack`. By default, these will be output to `./publish/`.
* @param {string[]} dotnetNugetSignArgs Arguments appended to `dotnet nuget sign`. You can also append '&&' if you want to start a new command or if you want to sign different sets of packages with different keys.
*/
export function configurePrepareCmd(
projectsToPublish: string[],
projectsToPackAndPush: string[] | false,
dotnetNugetSignArgs: string[] = ['./publish'],
) {
// These are later evaluated with MSBuild, but are passed via --framework and --runtime arguments instead of -p:TargetFramework
const publishProperties = MSBuildProjectPreDefinedProperties;
return [
formatDotnetPublish(projectsToPublish, publishProperties),
formatDotnetPack(projectsToPackAndPush),
formatDotnetNugetSign(dotnetNugetSignArgs)
].join(' && ');
}
export interface NuGetRegistryInfo {
tokenEnvVar: string;
url: string;
user?: string | undefined;
}
export const nugetDefault: NuGetRegistryInfo = {
tokenEnvVar: 'NUGET_TOKEN',
url: 'https://api.nuget.org/v3/index.json',
};
/**
* todo -
* @param nupkgDir
* @param registries
* @param pushToGitHub
* @returns
*/
export function configureDotnetNugetPush(
nupkgDir = './publish',
registries: NuGetRegistryInfo[] = [nugetDefault],
pushToGitHub = true,
): string {
if (registries.some((registry) => registry.url.trim() === ''))
throw new Error('The URL for one of the provided NuGet registries was empty or whitespace.');
// if user did not specify a GitHub NuGet Registry, try determine default values and add the Source.
if (pushToGitHub && !registries.some((reg) => reg.url.startsWith(nugetGitHubUrlBase))) {
const ghPair: NuGetRegistryInfo | undefined = getGithubNugetRegistryPair();
if (ghPair) {
registries.push(ghPair);
}
}
if (!registries.some((reg) => reg.url.startsWith(nugetGitHubUrlBase))) {
const glPair = getGitlabNugetRegistryPair();
if (glPair) {
registries.push(glPair);
}
}
return registries
.map(
(registry) => {
const tokenValue = getEnvVarValue(registry.tokenEnvVar);
ok(getEnvVarValue('SKIP_TOKEN') === 'true' || tokenValue, `The environment variable ${registry.tokenEnvVar} is undefined!`);
return `dotnet nuget push ${nupkgDir} --source ${registry.url} --token ${tokenValue ?? '**placeholder**'}`
}
)
.join(' && ');
}