Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
38cde77
added new deno settings
yunchengyang515 Nov 11, 2023
a18f3be
added new deno settings
yunchengyang515 Nov 11, 2023
544c7c6
Merge pull request #2 from stackql/dev/deno-vscode-setup
yunchengyang515 Nov 11, 2023
b05fe45
Created downloader and tests
yunchengyang515 Nov 12, 2023
26eede0
able to run query
yunchengyang515 Nov 12, 2023
55a1cdf
fix tests
yunchengyang515 Nov 12, 2023
b4799ca
attempt to fix file close
yunchengyang515 Nov 12, 2023
9186f8d
added multiple OS tests
yunchengyang515 Nov 12, 2023
2b5674b
WIP: fixing test issues
yunchengyang515 Nov 13, 2023
4a953cc
fix the Deno.run issue in unpacker
yunchengyang515 Nov 13, 2023
76a9569
replace deno.run with deno.command
yunchengyang515 Nov 13, 2023
db40abf
added allow execute for the binary
yunchengyang515 Nov 13, 2023
3fb7b6d
added more log to check windows OS issue
yunchengyang515 Nov 13, 2023
9063ab0
added more logging
yunchengyang515 Nov 13, 2023
4f8a442
fix the chomod issue
yunchengyang515 Nov 13, 2023
921aa5a
code clean up
yunchengyang515 Nov 13, 2023
d1ab0ae
Merge pull request #4 from stackql/dev/setup-github-test-action
yunchengyang515 Nov 13, 2023
29c1bf9
Merge pull request #3 from stackql/dev/install-stackql
jeffreyaven Nov 16, 2023
9d35ae3
added server connection and test
yunchengyang515 Nov 18, 2023
c529e14
added setup stackql in github action
yunchengyang515 Nov 18, 2023
77b2540
fix the process closing for server setup
yunchengyang515 Nov 18, 2023
b902d94
test only windows
yunchengyang515 Nov 18, 2023
c304703
added back linux and macos
yunchengyang515 Nov 18, 2023
526d095
add timeout to wait for stackql to start
yunchengyang515 Nov 18, 2023
19b0913
added retries
yunchengyang515 Nov 18, 2023
f6303df
remove timeout in start server
yunchengyang515 Nov 18, 2023
a2410da
WIP: return meaningful query results
yunchengyang515 Nov 18, 2023
83a3c58
use deno pgClient to get the correct result
yunchengyang515 Nov 18, 2023
78f892d
code cleanup
yunchengyang515 Nov 18, 2023
23846e3
remove unused import
yunchengyang515 Nov 18, 2023
417bbc9
Merge pull request #8 from stackql/dev/server-mode
yunchengyang515 Nov 18, 2023
74cfddd
wip: apply auth as param
yunchengyang515 Nov 19, 2023
64d1d39
removed auth string passing
yunchengyang515 Nov 19, 2023
f94df2b
Merge pull request #9 from stackql/dev/auth-params
yunchengyang515 Nov 19, 2023
c31ee4f
added tests for adding params and osUtils
yunchengyang515 Nov 25, 2023
eeb2931
fix tests
yunchengyang515 Nov 25, 2023
418e596
rename runClit to runCommand
yunchengyang515 Nov 25, 2023
eaf637a
Merge pull request #10 from stackql/dev/cli-properties
yunchengyang515 Nov 25, 2023
ff55c44
added proxy properties
yunchengyang515 Nov 25, 2023
0d2df97
created and added tests for upgrade
yunchengyang515 Nov 26, 2023
46ec053
test format fix
yunchengyang515 Nov 27, 2023
7f9aed5
fix spy issue in test
yunchengyang515 Nov 27, 2023
281f84a
wip: fix unpacker test in darwin
yunchengyang515 Nov 27, 2023
1e778ca
fix downloader tests with darwin
yunchengyang515 Dec 2, 2023
99f9924
Merge pull request #12 from stackql/dev/upgrade
yunchengyang515 Dec 3, 2023
a93a532
added csv and json output for run query on instnace
yunchengyang515 Dec 3, 2023
6ac3188
added new deno task runner for test
yunchengyang515 Dec 3, 2023
9574a59
added new deno task runner for test
yunchengyang515 Dec 3, 2023
ab25c55
wip: debugging failed test
yunchengyang515 Dec 4, 2023
6310cee
Merge branch 'release/v0.0.1' of https://github.com/stackql/stackqljs…
yunchengyang515 Dec 4, 2023
c4d90cd
Merge pull request #13 from stackql/dev/output-format
yunchengyang515 Dec 7, 2023
cf11480
added editor format on save
yunchengyang515 Dec 9, 2023
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
21 changes: 21 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Test

on:
push:

jobs:
test:
name: Test on Multiple OS
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Deno
uses: denoland/setup-deno@v1
with:
deno-version: v1.x
- name: Run tests
run: deno test --allow-net --allow-read --allow-write --allow-env --allow-run
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ yarn-error.log*

# typescript
*.tsbuildinfo
next-env.d.ts
next-env.d.ts
.stackql*
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,9 @@
- `upgrade` - if in local mode, upgrade the local stackql binary from the latest release
- `execute` - execute a query returns an array of rows (objects)
- `executeStmt` - executes a statement and returns a string (like `REGISTRY PULL` or `INSERT ...`)
- `executeQueriesAsync` - executes a list of queries and returns an array of rows (objects) - queries need to return the same columns/schema
- `executeQueriesAsync` - executes a list of queries and returns an array of rows (objects) - queries need to return the same columns/schema

## Test
```
deno test --allow-net --allow-read --allow-write --allow-env --allow-run <test file path>
```
1 change: 1 addition & 0 deletions deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { bold } from "https://deno.land/std/fmt/colors.ts"
6 changes: 2 additions & 4 deletions mod.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { bold } from "./deps.ts";
import { StackQL } from "./src/stackql.ts";

export function getHelloWorld(): string {
return bold("Hello World");
}
export { StackQL };
26 changes: 26 additions & 0 deletions src/services/downloader.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { assertExists } from "https://deno.land/std@0.206.0/assert/assert_exists.ts";
import { Downloader } from "./downloader.ts";
import { removeStackQLDownload } from "../../tests/utils.ts";

Deno.test("Downloader setupStackQL Integration Test", async () => {
// Arrange
await removeStackQLDownload();
const downloader = new Downloader();
let binaryPath: string;

// Act
try {
binaryPath = await downloader.setupStackQL();

// Assert
assertExists(binaryPath);
// Check if the binary exists after setupStackQL is called

console.log(
"Test passed: setupStackQL completed without errors and binary exists."
);
} catch (error) {
console.error("Test failed:", error);
throw error; // This will cause the test to fail
}
});
153 changes: 153 additions & 0 deletions src/services/downloader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { join } from "https://deno.land/std@0.133.0/path/mod.ts";
import { SupportedOs } from "../types/platforms.ts";
import { darwinUnpack, unzip } from "./unpacker.ts";
import { chomod } from "../utils.ts";

export class Downloader {
private os: string;
private arch: string;
private urlMap: Record<string, string>;

constructor() {
this.os = Deno.build.os; // 'linux', 'darwin', or 'windows'
this.arch = Deno.build.arch; // 'x86_64', 'arm64', etc.

this.urlMap = {
[SupportedOs.Linux]:
"https://releases.stackql.io/stackql/latest/stackql_linux_amd64.zip",
[SupportedOs.Windows]:
"https://releases.stackql.io/stackql/latest/stackql_windows_amd64.zip",
[SupportedOs.Darwin]:
"https://storage.googleapis.com/stackql-public-releases/latest/stackql_darwin_multiarch.pkg",
// Additional OS-architecture combinations can be added here
};
}

private async downloadFile(url: string, downloadDir: string) {
const res = await fetch(url);
const file = await Deno.open(downloadDir, { create: true, write: true });

try {
await res.body?.pipeTo(file.writable).finally(
() => file.close() //TODO: fix bad resource id when closing file
);
} catch (error) {
console.error(`ERROR: [downloadFile] ${error.message}`);
}

console.log("Closed file");
}

private getUrl(): string {
const key = `${this.os}`;
const url = this.urlMap[key];

if (!url) {
throw new Error(`Unsupported OS type: ${this.os}`);
}

return url;
}

/**
* Gets binary name
* @returns binrary name
*/
private getBinaryName() {
const binaryMap: Record<SupportedOs, string> = {
[SupportedOs.Windows]: "stackql.exe",
[SupportedOs.Darwin]: "stackql/Payload/stackql",
[SupportedOs.Linux]: "stackql", // Default case for Linux and other platforms
};
const os = Deno.build.os.toLowerCase();

if (!Object.values(SupportedOs).includes(os as SupportedOs)) {
throw new Error(`Unsupported OS type: ${os}`);
}

const binaryOs = os as SupportedOs;
return binaryMap[binaryOs];
}

/**
* Gets download dir
* @returns download dir
*/
private async getDownloadDir(): Promise<string> {
const projectDir = Deno.cwd();
console.log("Project dir:", projectDir);

if (!projectDir) {
throw new Error("Unable to determine the project directory.");
}

const downloadDir = join(projectDir, ".stackql");

try {
const stat = await Deno.stat(downloadDir);
if (!stat.isDirectory) {
await Deno.mkdir(downloadDir, { recursive: true });
}
} catch (error) {
if (error instanceof Deno.errors.NotFound) {
await Deno.mkdir(downloadDir, { recursive: true });
} else {
throw error;
}
}

return downloadDir;
}

private binaryExists(binaryName: string, downloadDir: string): boolean {
const binPath = join(downloadDir, binaryName);
try {
Deno.statSync(binPath);
return true;
} catch (error) {
if (error instanceof Deno.errors.NotFound) {
return false;
}
throw error;
}
}
private async installStackQL(downloadDir: string) {
const url = this.getUrl();

const archiveFileName = `${downloadDir}/${url.split("/").pop()}`;
await this.downloadFile(url, archiveFileName);

console.log("Unpacking stackql binary");
const unpacker = Deno.build.os === "darwin" ? darwinUnpack : unzip;
await unpacker({ downloadDir, archiveFileName });
}
private async setExecutable(binaryPath: string) {
const allowExecOctal = 0o755;
await chomod(binaryPath, allowExecOctal);
}
/**
* Setup stackql binary, check if binary exists, if not download it
*/
public async setupStackQL() {
console.log("Installing stackql...");

try {
const binaryName = this.getBinaryName();
const downloadDir = await this.getDownloadDir();
const binaryPath = join(downloadDir, binaryName);
if (this.binaryExists(binaryName, downloadDir)) {
console.log("stackql is already installed");
await this.setExecutable(binaryPath);
return binaryPath;
}

console.log("Downloading stackql binary");
await this.installStackQL(downloadDir);
await this.setExecutable(binaryPath);
return binaryPath;
} catch (error) {
console.error(`ERROR: [setup] ${error.message}`);
Deno.exit(1);
}
}
}
37 changes: 37 additions & 0 deletions src/services/unpacker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { decompress } from "https://deno.land/x/zip/mod.ts";

type UnpackParams = {
downloadDir: string;
archiveFileName: string;
};

export const darwinUnpack = async (params: UnpackParams) => {
console.log("darwinUnpack");
const { downloadDir, archiveFileName } = params;
const unpackedFileName = `${downloadDir}/stackql`;
const commandPath = "pkgutil";
const commandArgs = ["--expand-full", archiveFileName, unpackedFileName];
const process = new Deno.Command(commandPath, {
args: commandArgs,
stdout: "piped",
stderr: "piped",
});

const { code, stdout, stderr } = await process.output();
if (code !== 0) {
const output = new TextDecoder().decode(stdout);
const errorOutput = new TextDecoder().decode(stderr);
console.error("Error executing pkgutil:", output, errorOutput);
throw new Error("Failed to unpack stackql");
}
};

export const unzip = async (params: UnpackParams) => {
console.log("unzip");
try {
await decompress(params.archiveFileName, params.downloadDir);
} catch (error) {
console.log("[unzip] error:", error);
throw new Error("Failed to unpack stackql");
}
};
36 changes: 36 additions & 0 deletions src/stackql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { assertExists } from "https://deno.land/std@0.206.0/assert/assert_exists.ts";
import { Downloader } from "./services/downloader.ts";

export class StackQL {
private binaryPath?: string;
private downloader: Downloader = new Downloader();
constructor() {}
private async initialize() {
this.binaryPath = await this.downloader.setupStackQL();
}

public async runQuery(query: string) {
if (!this.binaryPath) {
await this.initialize();
assertExists(this.binaryPath);
}

const process = new Deno.Command(this.binaryPath, {
args: ["exec", query], // Ensure this command is correct
stdout: "piped",
stderr: "piped",
});

const { code, stdout, stderr } = await process.output();

if (code === 0) {
const output = stdout;
const result = new TextDecoder().decode(output);
return result;
} else {
const errorOutput = stderr;
const errorMessage = new TextDecoder().decode(errorOutput);
throw new Error(`StackQL query failed: ${errorMessage}`);
}
}
}
5 changes: 5 additions & 0 deletions src/types/platforms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum SupportedOs {
Linux = "linux",
Darwin = "darwin",
Windows = "windows",
}
17 changes: 17 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const fileExists = (path: string) => {
try {
Deno.statSync(path);
return true;
} catch (error) {
if (error instanceof Deno.errors.NotFound) {
return false;
}
throw error;
}
};

export const chomod = async (path: string, mode: number) => {
if (Deno.build.os !== "windows") {
await Deno.chmod(path, mode);
}
};
20 changes: 20 additions & 0 deletions tests/stackql.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { assertStringIncludes } from "https://deno.land/std@0.206.0/assert/mod.ts";
import { StackQL } from "../src/stackql.ts";
import { removeStackQLDownload } from "./utils.ts";

Deno.test("StackQL runQuery - Successful Execution", async () => {
// Arrange
await removeStackQLDownload();
const stackQL = new StackQL();
const pullQuery = "REGISTRY PULL okta;";
const testQuery = "SHOW PROVIDERS"; // Replace with a valid query for your context

// Act
await stackQL.runQuery(pullQuery);
const result = await stackQL.runQuery(testQuery);

// Assert
assertStringIncludes(result, "name");
assertStringIncludes(result, "version");
assertStringIncludes(result, "okta");
});
13 changes: 13 additions & 0 deletions tests/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { existsSync } from "https://deno.land/std/fs/mod.ts";
import { join } from "https://deno.land/std@0.133.0/path/mod.ts";

export const removeStackQLDownload = async () => {
const projectDir = Deno.cwd();
const stackqlPath = join(projectDir, ".stackql");
console.log("stackqlPath", stackqlPath);
console.log("existsSync(stackqlPath)", existsSync(stackqlPath));
if (existsSync(stackqlPath)) {
console.log("Removing .stackql directory");
await Deno.remove(stackqlPath, { recursive: true });
}
};