Skip to content

Commit

Permalink
Merge pull request #35 from mikkelhegn/main
Browse files Browse the repository at this point in the history
Always install latest version - and also some strcutured logging
  • Loading branch information
mikkelhegn authored Nov 11, 2024
2 parents 1b22ddc + 6a0eef1 commit 6fb965d
Show file tree
Hide file tree
Showing 8 changed files with 989 additions and 519 deletions.
1,341 changes: 869 additions & 472 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
"webpack-cli": "^4.9.1"
},
"dependencies": {
"@octokit/rest": "^21.0.2",
"extract-zip": "^2.0.1",
"got": "^11.8.3",
"mkdirp": "^1.0.4",
Expand Down
12 changes: 6 additions & 6 deletions src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as spin from '../spin';
import { shell } from '../utils/shell';
import { isErr } from '../errorable';
import { longRunningProcess } from '../longrunning';
import * as output from '../output';
import * as log from '../logger';
import { promptLogin } from '../fermyon/environment-ui';

export async function deploy() {
Expand Down Expand Up @@ -38,15 +38,15 @@ export async function deploy() {
}

if (isErr(deployResult)) {
output.appendLine(`Spin ${description} failed. Details:`);
output.appendLine(deployResult.message);
output.appendLine('');
log.error(deploy.name, `Spin ${description} failed. Details:`);
log.error(deploy.name, deployResult.message);
log.error(deploy.name, '');
await vscode.window.showErrorMessage(`Spin ${description} failed. Error: ${deployResult.message}`);
return;
}

output.appendLine(deployResult.value);
output.show();
log.info(deploy.name, deployResult.value);
log.show();
const message = `Spin ${description} complete. See output pane for application URL.`;
await vscode.window.showInformationMessage(message);
}
58 changes: 43 additions & 15 deletions src/installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,45 @@ import * as config from './config';
import { Errorable, err, ok, isErr, isOk } from "./errorable";
import * as layout from './layout';
import { longRunning } from './longrunning';
import * as log from './logger';
import { Octokit } from "@octokit/rest";

const SPIN_VERSION = "0.8.0";
const SPIN_DONWLOAD_URL_TEMPLATE = `https://github.com/fermyon/spin/releases/download/v${SPIN_VERSION}/spin-v${SPIN_VERSION}-{{subst:os}}-{{subst:arch}}.{{subst:fmt}}`;
// Fallback version is used if the call to getLatestReleases() fails
const SPIN_VERSION_FALLBACK = "3.0.0";
const SPIN_TOOL_NAME = "spin";
const SPIN_BIN_NAME = "spin";

export async function ensureSpinInstalled(): Promise<Errorable<string>> {
const spinVersion = await getLatestReleases();
log.info(ensureSpinInstalled.name, `Checking if Spin ${spinVersion} is installed`);

const customPath = config.customPath();
if (customPath) {
log.info(ensureSpinInstalled.name, `Using the following custom path from configuration: ${customPath}`);
return ok(customPath);
}

const toolFile = installLocation(SPIN_TOOL_NAME, SPIN_BIN_NAME);
if (!fs.existsSync(toolFile) || !isInstallCurrent()) {
const downloadResult = await longRunning(`Downloading Spin ${SPIN_VERSION}...`, () =>
downloadSpinTo(toolFile)
log.info(ensureSpinInstalled.name, `Checking for Spin at: ${toolFile}`);
if (!fs.existsSync(toolFile) || !isInstallCurrent(spinVersion)) {
log.info(ensureSpinInstalled.name, `Didn't find Spin locally.`);
const downloadResult = await longRunning(`Downloading Spin ${spinVersion}...`, () =>
downloadSpinTo(toolFile, spinVersion)
);
if (isErr(downloadResult)) {
log.error(ensureSpinInstalled.name, `Error installing Spin: ${JSON.stringify(downloadResult)}`);
return downloadResult;
}
}
markInstallCurrent();
}
markInstallCurrent(spinVersion);
log.info(ensureSpinInstalled.name, `Spin installed at: ${toolFile}`);
return ok(toolFile);
}

async function downloadSpinTo(toolFile: string): Promise<Errorable<null>> {
async function downloadSpinTo(toolFile: string, spinVersion: string): Promise<Errorable<null>> {
const toolDir = path.dirname(toolFile);

const sourceUrl = downloadSource();
const sourceUrl = downloadSource(spinVersion);
if (isErr(sourceUrl)) {
return sourceUrl;
}
Expand All @@ -56,7 +66,7 @@ async function downloadSpinTo(toolFile: string): Promise<Errorable<null>> {
return unarchiveResult;
}

function downloadSource(): Errorable<string> {
function downloadSource(spinVersion: string): Errorable<string> {
const osId = os();
const archId = arch();
const fmtId = fmt();
Expand All @@ -65,7 +75,8 @@ function downloadSource(): Errorable<string> {
return err("Unsupported operating system or processor architecture");
}

const url = SPIN_DONWLOAD_URL_TEMPLATE.replace("{{subst:os}}", osId).replace("{{subst:arch}}", archId).replace("{{subst:fmt}}", fmtId);
const url = `https://github.com/fermyon/spin/releases/download/${spinVersion}/spin-${spinVersion}-${osId}-${archId}.${fmtId}`;
log.info(downloadSource.name, `"Download URI: ${url}`);
return ok(url);
}

Expand All @@ -79,18 +90,18 @@ export function installLocation(tool: string, bin: string): string {
return toolFile;
}

function isInstallCurrent(): boolean {
function isInstallCurrent(spinVersion: string): boolean {
const versionFile = installedVersionLocation(SPIN_TOOL_NAME);
if (!fs.existsSync(versionFile)) {
return false;
}
const text = fs.readFileSync(versionFile, { encoding: 'utf-8' });
return text.trim() === SPIN_VERSION;
return text.trim() === spinVersion;
}

function markInstallCurrent() {
function markInstallCurrent(spinVersion: string) {
const versionFile = installedVersionLocation(SPIN_TOOL_NAME);
fs.writeFileSync(versionFile, SPIN_VERSION, { encoding: 'utf-8' });
fs.writeFileSync(versionFile, spinVersion, { encoding: 'utf-8' });
}

function installedVersionLocation(tool: string): string {
Expand Down Expand Up @@ -185,3 +196,20 @@ async function unzip(sourceFile: string, destinationFolder: string): Promise<Err
return err("zip extract failed");
}
}

async function getLatestReleases(): Promise<string> {
const octokit = new Octokit();
const { data: release } = await octokit.rest.repos.getLatestRelease({
owner: "fermyon",
repo: "spin",
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
});

if (release.name === null) {
return SPIN_VERSION_FALLBACK;
} else {
return release.name;
}
}
66 changes: 66 additions & 0 deletions src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as vscode from 'vscode';

const OUTPUT_CHANNEL = vscode.window.createOutputChannel("Spin", );

let shownInstallErrorToast = false;

enum logLevel {
Info = "Info",
Warning = "Warning",
Error = "Error",
}

interface logMessage {
level: logLevel,
function: string,
message: string,
}

export function info(caller: string, message: string) {
const log: logMessage = {
level:logLevel.Info,
function: caller,
message: message
};
OUTPUT_CHANNEL.appendLine(formatLog(log));
}

export function warning(caller: string, message: string) {
const log: logMessage = {
level:logLevel.Warning,
function: caller,
message: message
};
OUTPUT_CHANNEL.appendLine(formatLog(log));
}

export function error(caller: string, message: string) {
const log: logMessage = {
level:logLevel.Error,
function: caller,
message: message
};
OUTPUT_CHANNEL.appendLine(formatLog(log));
}

export function logMessage(logMessage: logMessage) {
OUTPUT_CHANNEL.appendLine(formatLog(logMessage));
}

export function warnInstallNotEnsured(message: string, always?: 'always' | 'if-not-shown') {
if (!shownInstallErrorToast || always === 'always') {
shownInstallErrorToast = true;
vscode.window.showWarningMessage(message);
} else {
warning(warnInstallNotEnsured.name, message);
}
}

export function show(preserveFocus?: boolean) {
OUTPUT_CHANNEL.show(preserveFocus);
}

function formatLog(log: logMessage): string {
const date = new Date().toISOString();
return `${date} [${log.level}] ${log.message}`;
}
22 changes: 0 additions & 22 deletions src/output.ts

This file was deleted.

6 changes: 3 additions & 3 deletions src/spin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { CancellationToken } from 'vscode';
import { Errorable, isErr, ok } from './errorable';
import { ensureSpinInstalled } from './installer';
import * as shell from './utils/shell';
import * as output from './output';
import * as log from './logger';

async function invokeObj<T>(sh: shell.Shell, command: string, args: string, opts: shell.ExecOpts, fn: (stdout: string) => T): Promise<Errorable<T>> {
const binOpt = await ensureSpinInstalled();
Expand All @@ -12,7 +12,7 @@ async function invokeObj<T>(sh: shell.Shell, command: string, args: string, opts
const bin = binOpt.value;

const cmd = `${bin} ${command} ${args}`;
output.appendLine(`$ ${cmd}`);
log.info(invokeObj.name, `$ ${cmd}`);
return await sh.execObj<T>(
cmd,
`spin ${command}`,
Expand All @@ -23,7 +23,7 @@ async function invokeObj<T>(sh: shell.Shell, command: string, args: string, opts

function andLog<T>(fn: (s: string) => T): (s: string) => T {
return (s: string) => {
output.appendLine(s);
log.info(andLog.name, s);
return fn(s);
};
}
Expand Down
2 changes: 1 addition & 1 deletion src/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as vscode from 'vscode';
import { isOk } from './errorable';

import * as installer from './installer';
import { warnInstallNotEnsured } from './output';
import { warnInstallNotEnsured } from './logger';
import { cantHappen } from './utils/never';

const TASK_SOURCE = "spin";
Expand Down

0 comments on commit 6fb965d

Please sign in to comment.