Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fake5 Runner task #16

Merged
merged 7 commits into from
Aug 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,15 @@ _ReSharper*/
*.vsix
publish.bat

*.js

.fake
node_modules
SetPaketCredentialProvider.dev/CredentialProvider/
SetPaketCredentialProvider/
FAKE5/
FAKE5Vault/
PaketCredentialCleanup/
PaketRestore/
/.vs
_build
25 changes: 25 additions & 0 deletions Common/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

"use strict";

import * as semver from "semver";
import * as credMgr from "./paketCredMgr";
import * as vault from "./myvault";
import * as fake5 from "./fake5";

export async function setupPaketCredentialManager() {
await credMgr.setup();
}

export async function cleanupPaketCredentialManager() {
await credMgr.cleanup();
}

export type Vault = vault.Vault

export function createFakeVariablesJson(secretPath : string, filterSecrets : boolean) {
return fake5.createFakeVariablesJson(secretPath, filterSecrets);
}

export function downloadFakeAndReturnInvocation(fakeVersion:semver.SemVer, fakeArgs : string) {
return fake5.downloadAndReturnInvocation(fakeVersion, fakeArgs);
}
117 changes: 117 additions & 0 deletions Common/fake5.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@

import * as path from "path";
import * as fs from "fs";
import * as tl from "vsts-task-lib";
import * as semver from "semver";
import * as rm from 'typed-rest-client';
import * as toolLib from 'vsts-task-tool-lib/tool';
import * as ghTyped from "./githubTyped";
import * as vault from "./myvault";
import { isNullOrUndefined } from "util";

interface Variables {
keyFile : string;
iv : string;
values : tl.VariableInfo[];
}

export function createFakeVariablesJson(secretFile : string, filterSecrets : boolean) {
let v = new vault.Vault(secretFile);
let variables : tl.VariableInfo[] =
tl.getVariables()
.map((entry, _) => {
if (entry.secret) {
let encrypted = v.encryptSecret(entry.value);
return <tl.VariableInfo>{ secret: true, value: encrypted, name: entry.name };
} else {
return <tl.VariableInfo>{ secret: false, value: entry.value, name: entry.name }
}
})
.filter((entry, _) =>{
if (filterSecrets && entry.secret) {
return false;
} else {
return true;
}
});
let json = JSON.stringify(<Variables>{
keyFile: secretFile,
iv: v.retrieveIVBase64(),
values: variables });
return json;
}


let toolCacheName = "fake";

async function retrieveVersionEntry(targetVersion : semver.SemVer){
let rest = new rm.RestClient('fake-vsts-task', 'https://api.github.com/');
async function getPage(pageNum:number){
let result = await rest.get<ghTyped.RootObject[]>(`repos/fsharp/fake/releases?page=${pageNum}`);
if (result.statusCode != 200) {
tl.warning(`retrieved status code '${result.statusCode}' from page '${pageNum}'`)
} else {
tl.debug(`retrieved status code '${result.statusCode}' from page '${pageNum}'`);
}
return result;
}

let pageNr = 1;
let currentPage = await getPage(pageNr);
while (currentPage.statusCode == 200 && currentPage.result.length > 0) {
let result = currentPage.result.find((val) => {
let version = semver.parse(val.name);
if (!version){
version = semver.parse(val.tag_name);
}

return targetVersion.compare(version) == 0;
});

if (result) {
return result;
} else {
pageNr = pageNr + 1;
currentPage = await getPage(pageNr);
}
}

return null;
}

async function downloadPortableFake(targetVersion : semver.SemVer) {
let release = await retrieveVersionEntry(targetVersion);
if (!release) {
return null;
}

let portableAsset = release.assets.find(asset => asset.name == "fake-dotnetcore-portable.zip");
let downloadPath = await toolLib.downloadTool(portableAsset.browser_download_url);
let extPath = await toolLib.extractZip(downloadPath);
return await toolLib.cacheDir(extPath, toolCacheName, targetVersion.raw);
}

export async function downloadAndReturnInvocation(fakeVersion:semver.SemVer, fakeArgs : string) {
let dotnetExecutable = tl.which("dotnet");
if (!dotnetExecutable) {
// In the future we could download the not-portable version here...
tl.error("Require a `dotnet` executable in PATH for this task, consider using the 'DotNetCoreInstaller' task before this one.");
return null;
}

let toolPath = toolLib.findLocalTool(toolCacheName, fakeVersion.raw);
if (!toolPath){
toolPath = await downloadPortableFake(fakeVersion);
if(!toolPath) {
tl.error(`Version '${fakeVersion.raw}' was not found in the official releases.`);
return null;
}
}

let fakeDll = path.join(toolPath, "fake.dll");
if (isNullOrUndefined(fakeArgs) || fakeArgs === "") {
return [ dotnetExecutable, fakeDll ]
} else {
return [ dotnetExecutable, `${fakeDll} ${fakeArgs}`]
}
}
80 changes: 80 additions & 0 deletions Common/githubTyped.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// from http://json2ts.com/

export interface Author {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
}

export interface Uploader {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
}

export interface Asset {
url: string;
browser_download_url: string;
id: number;
node_id: string;
name: string;
label: string;
state: string;
content_type: string;
size: number;
download_count: number;
created_at: Date;
updated_at: Date;
uploader: Uploader;
}

export interface RootObject {
url: string;
html_url: string;
assets_url: string;
upload_url: string;
tarball_url: string;
zipball_url: string;
id: number;
node_id: string;
tag_name: string;
target_commitish: string;
name: string;
body: string;
draft: boolean;
prerelease: boolean;
created_at: Date;
published_at: Date;
author: Author;
assets: Asset[];
}
53 changes: 53 additions & 0 deletions Common/myvault.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

// This is a defensive copy from https://raw.githubusercontent.com/Microsoft/vsts-task-lib/91e03a14f1188edc658954320f9d75565b2d1da5/node/vault.ts
// To save ourself from breaking changes...
// Add some members for retrieving the decrypted value and use IV.

import fs = require('fs');
import crypto = require('crypto');

var algorithm = "aes-256-ctr";

//
// Store sensitive data in proc.
// Main goal: Protects tasks which would dump envvars from leaking secrets inadvertently
// the task lib clears after storing.
// Also protects against a dump of a process getting the secrets
// The secret is generated and stored externally for the lifetime of the task.
//
export class Vault {
constructor(keyFile: string) {
this._keyFile = keyFile;
this.genKey();
this._iv = crypto.randomBytes(16);
}

private _keyFile: string;
private _iv: Buffer;

public initialize(): void {

}

public encryptSecret(data: string) : string {
var key = this.getKey();
var cipher = crypto.createCipheriv(algorithm, key, this._iv);
var crypted = cipher.update(data,'utf8','base64')
crypted += cipher.final('base64');
return crypted;
}

public retrieveIVBase64(): string {
return this._iv.toString('base64');
}

private getKey() {
let readData = fs.readFileSync(this._keyFile, 'utf8');
return Buffer.from(readData, 'base64');
}

private genKey(): void {
let base64String = crypto.randomBytes(32).toString('base64');
fs.writeFileSync(this._keyFile, base64String, { encoding: 'utf8' });
}
}
Loading