Skip to content

Commit

Permalink
feat: port of PoC and setting up
Browse files Browse the repository at this point in the history
  • Loading branch information
daveleek committed Aug 31, 2023
1 parent 1600f8b commit 5c01a92
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 150 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ out

# Nuxt.js build / generate output
.nuxt
dist
build

# Gatsby files
.cache/
Expand Down
38 changes: 38 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: 'Unleash Feature Flags'
description: 'Lets you use the getunleash.io feature flags management solution in GitHub Actions'
inputs:
app-name:
description: 'The application name for the GitHub Action as you want it to appear in Unleash metrics'
type: 'string'
required: true
api-key:
description: 'The frontend API token to use with Unleash'
type: 'string'
required: true
url:
description: 'Url to Unleash proxy or frontend api'
type: 'string'
required: true
environment:
description: 'The environment for which to evaluate the feature flags'
type: 'string'
required: false
default: 'default'
is-enabled:
description: 'Newline-separated list of feature flag names to evaluate'
type: 'string'
required: false
get-variant:
description: 'Newline-separated list of feature flag names to get variants for'
type: 'string'
required: false
context:
description: 'Multiline list of key=value pairs that will be string split on ='
type: 'string'
required: false
outputs:
the-value:
description: 'What we found in the input'
runs:
using: 'node20'
main: 'dist/index.js'
39 changes: 7 additions & 32 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,46 +7,21 @@
"license": "Apache-2.0",
"scripts": {
"build": "tsc",
"release": "tsc --outDir dist",
"release": "ncc build src/index.ts --license licenses.txt -o dist",
"test": "jest"
},
"jest": {
"automock": false,
"maxWorkers": 4,
"testTimeout": 10000,
"globalSetup": "./scripts/jest-setup.js",
"transform": {
"^.+\\.tsx?$": [
"@swc/jest"
]
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
"testPathIgnorePatterns": [
"/dist/",
"/node_modules/"
],
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json"
],
"coveragePathIgnorePatterns": [
"/node_modules/",
"/dist/"
]
},
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1"
"@actions/github": "^5.1.1",
"@types/node": "^20.5.7",
"node": "^20.5.1",
"node-fetch": "^3.3.2",
"unleash-proxy-client": "^2.5.0"
},
"devDependencies": {
"@swc/core": "^1.3.80",
"@swc/jest": "^0.2.29",
"@types/jest": "^29.5.4",
"@vercel/ncc": "^0.36.1",
"jest": "^29.6.4",
"prettier": "^3.0.3",
"typescript": "^5.2.2"
}
}
30 changes: 30 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { createUnleashAction } from "./unleash-action";
import { getInput, getMultilineInput, setOutput } from '@actions/core';

const appName = getInput('app-name');
const url = getInput('url');
const clientKey = getInput('api-key');
const environment = getInput('environment');

const context: Record<string, string> = {};
const contextLines = getMultilineInput('context');
contextLines?.forEach(l => {
let keyVal = l.split('=');
context[keyVal[0]] = keyVal[1];
});

const features = getMultilineInput('is-enabled');
const variants = getMultilineInput('get-variant');

createUnleashAction({
url: url,
clientKey: clientKey,
appName: appName,
environment: environment,
context: context,
features: features,
variants: variants,
setResult: setOutput
}).then(() => {
console.log("Done!");
});
107 changes: 107 additions & 0 deletions src/unleash-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { UnleashClient } from "unleash-proxy-client";
import Metrics from "unleash-proxy-client/build/metrics";
import fetch from "node-fetch";

interface IUnleashActionOptions {
url: string;
clientKey: string;
appName: string;
environment: string;
context: Record<string, string>;
features?: string[];
variants?: string[];
setResult: (name: string, value: any) => void;
}

export const createUnleashAction = async (
options: IUnleashActionOptions
): Promise<void> => {
const action = new UnleashAction(options);
await action.run();
await action.end();
};

export class UnleashAction {
private unleash: UnleashClient;
private metrics: Metrics;
private features: string[];
private variants: string[];
private setResult: (name: string, value: any) => void;

constructor(options: IUnleashActionOptions) {
this.unleash = this.createClient(options);
this.unleash.on("ready", () => {
console.log("Ready!");
});

this.metrics = this.createMetrics(options);

this.features = options.features || [];
this.variants = options.variants || [];
this.setResult = options.setResult;
}

async run(): Promise<void> {
console.log("starting.");
await this.unleash.start();

console.log("Checking features.");
await this.checkFeatures();

console.log("Checking variants.");
await this.checkVariants();
}

async end(): Promise<void> {
console.log("Sending metrics.");
await this.metrics.sendMetrics();

console.log("Stopping.");
await this.unleash.stop();
}

private createClient(options: IUnleashActionOptions): UnleashClient {
return new UnleashClient({
appName: options.appName,
url: options.url,
clientKey: options.clientKey,
environment: options.environment,
refreshInterval: 0,
metricsInterval: 0,
disableMetrics: true,
});
}

private createMetrics(options: IUnleashActionOptions): Metrics {
return new Metrics({
fetch: fetch,
headerName: "Authorization",
onError: (e) => {},
appName: options.appName,
url: options.url,
clientKey: options.clientKey,
disableMetrics: false,
metricsInterval: 0,
});
}

private async checkFeatures(): Promise<void> {
this.features.forEach((featureName) => {
const isEnabled = this.unleash.isEnabled(featureName);
this.metrics.count(featureName, isEnabled);
this.setResult(featureName, isEnabled);
});
}

private async checkVariants(): Promise<void> {
this.variants.forEach((featureName) => {
const variant = this.unleash.getVariant(featureName);
if (variant.name) {
this.metrics.countVariant(featureName, variant.name);
}
this.metrics.count(featureName, variant.enabled);
this.setResult(featureName, variant.enabled);
this.setResult(`${featureName}_variant`, variant.payload?.value);
});
}
}
Loading

0 comments on commit 5c01a92

Please sign in to comment.