Skip to content

Commit 429ec09

Browse files
Merge pull request #14 from stackql/minor-fix/add-fmt-on-save
Minor fix/add fmt on save
2 parents ebd2064 + cf11480 commit 429ec09

19 files changed

Lines changed: 1224 additions & 12 deletions

File tree

.github/workflows/test.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
6+
jobs:
7+
test:
8+
name: Test on Multiple OS
9+
runs-on: ${{ matrix.os }}
10+
strategy:
11+
matrix:
12+
os: [ubuntu-latest, windows-latest, macos-latest]
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v3
16+
- name: Set up Deno
17+
uses: denoland/setup-deno@v1
18+
with:
19+
deno-version: v1.x
20+
- name: setup StackQL
21+
uses: stackql/setup-stackql@v2.0.0
22+
with:
23+
use_wrapper: false
24+
- name: Run tests
25+
run: deno test --allow-net --allow-read --allow-write --allow-env --allow-run

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@ yarn-error.log*
3333

3434
# typescript
3535
*.tsbuildinfo
36-
next-env.d.ts
36+
next-env.d.ts
37+
.stackql*

.vscode/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"deno.enable": true,
3+
"deno.lint": true,
4+
"deno.unstable": true,
5+
"editor.formatOnSave": true,
6+
"editor.defaultFormatter": "denoland.vscode-deno"
7+
}

README.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
# stackqljs
77

8-
[__StackQL__](https://github.com/stackql/stackql) client library for Deno and Node.js that exposes all features StackQL.
8+
[**StackQL**](https://github.com/stackql/stackql) client library for Deno and Node.js that exposes all features StackQL.
99

1010
## Requirements
1111

@@ -14,8 +14,18 @@
1414
- No dependencies
1515
- Works in server mode (as a pg wire protocol server client) as well as local mode where it is a wrapper for the `stackql` binary for the target platform
1616
- Exposes methods analagous to [`pystackql`](https://pystackql.readthedocs.io/en/latest/), including:
17-
- `connect` - connect to a StackQL server - if in server mode
18-
- `upgrade` - if in local mode, upgrade the local stackql binary from the latest release
19-
- `execute` - execute a query returns an array of rows (objects)
20-
- `executeStmt` - executes a statement and returns a string (like `REGISTRY PULL` or `INSERT ...`)
21-
- `executeQueriesAsync` - executes a list of queries and returns an array of rows (objects) - queries need to return the same columns/schema
17+
- `connect` - connect to a StackQL server - if in server mode
18+
- `upgrade` - if in local mode, upgrade the local stackql binary from the latest release
19+
- `execute` - execute a query returns an array of rows (objects)
20+
- `executeStmt` - executes a statement and returns a string (like `REGISTRY PULL` or `INSERT ...`)
21+
- `executeQueriesAsync` - executes a list of queries and returns an array of rows (objects) - queries need to return the same columns/schema
22+
23+
## Test
24+
25+
### Requirement
26+
27+
- To run the tests locally, [install StackQL](https://stackql.io/docs/installing-stackql) first.
28+
29+
```
30+
deno task test <optional: test file path>
31+
```

deno.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"lint": {
3+
"include": ["src/"],
4+
"exclude": ["src/testing/", "src/**/*.test.ts", "src/*test.ts/"],
5+
"rules": {
6+
"tags": ["recommended"],
7+
"include": [],
8+
"exclude": ["no-unused-vars", "ban-untagged-todo"]
9+
}
10+
},
11+
"fmt": {
12+
"useTabs": true,
13+
"lineWidth": 80,
14+
"indentWidth": 4,
15+
"semiColons": false,
16+
"singleQuote": true,
17+
"proseWrap": "preserve",
18+
"include": ["src/"],
19+
"exclude": ["src/testdata/", "data/fixtures/**/*.ts"]
20+
},
21+
"tasks": {
22+
"test": "deno test --allow-net --allow-read --allow-write --allow-env --allow-run"
23+
}
24+
}

deno.lock

Lines changed: 178 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

deps.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { bold } from "https://deno.land/std@0.206.0/fmt/colors.ts";
1+
export { bold } from "https://deno.land/std/fmt/colors.ts"

mod.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { bold } from "./deps.ts";
1+
import { StackQL } from "./src/stackql.ts";
22

3-
export function getHelloWorld(): string {
4-
return bold("Hello World");
5-
}
3+
export { StackQL };

src/services/downloader.test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { assertExists } from 'https://deno.land/std@0.206.0/assert/assert_exists.ts'
2+
import { Downloader } from './downloader.ts'
3+
import { removeStackQLDownload } from '../../testing/utils.ts'
4+
import {
5+
assertSpyCalls,
6+
spy,
7+
} from 'https://deno.land/std@0.207.0/testing/mock.ts'
8+
9+
Deno.test('Downloader setupStackQL and upgrade Test', async () => {
10+
// Arrange
11+
await removeStackQLDownload()
12+
const downloader = new Downloader()
13+
let binaryPath: string
14+
const denoOpenSpy = spy(Deno, 'open')
15+
16+
// Act
17+
const setupTest = async () => {
18+
try {
19+
binaryPath = await downloader.setupStackQL()
20+
21+
// Assert
22+
assertExists(binaryPath)
23+
assertSpyCalls(denoOpenSpy, 1)
24+
// Check if the binary exists after setupStackQL is called
25+
26+
console.log(
27+
'Test passed: setupStackQL completed without errors and binary exists.',
28+
)
29+
} catch (error) {
30+
console.error('Test failed:', error)
31+
throw error // This will cause the test to fail
32+
}
33+
}
34+
35+
const upgradeTest = async () => {
36+
try {
37+
binaryPath = await downloader.upgradeStackQL()
38+
39+
assertExists(binaryPath)
40+
assertSpyCalls(denoOpenSpy, 2)
41+
} catch (error) {
42+
console.error('Test failed:', error)
43+
throw error // This will cause the test to fail
44+
}
45+
}
46+
47+
await setupTest()
48+
await upgradeTest()
49+
})

src/services/downloader.ts

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import { join } from 'https://deno.land/std@0.133.0/path/mod.ts'
2+
import { SupportedOs } from '../types/platforms.ts'
3+
import { darwinUnpack, unzip } from './unpacker.ts'
4+
import osUtils from '../utils/os.ts'
5+
6+
export class Downloader {
7+
private os: string
8+
private arch: string
9+
private urlMap: Record<string, string>
10+
constructor() {
11+
this.os = Deno.build.os // 'linux', 'darwin', or 'windows'
12+
this.arch = Deno.build.arch // 'x86_64', 'arm64', etc.
13+
14+
this.urlMap = {
15+
[SupportedOs.Linux]:
16+
'https://releases.stackql.io/stackql/latest/stackql_linux_amd64.zip',
17+
[SupportedOs.Windows]:
18+
'https://releases.stackql.io/stackql/latest/stackql_windows_amd64.zip',
19+
[SupportedOs.Darwin]:
20+
'https://storage.googleapis.com/stackql-public-releases/latest/stackql_darwin_multiarch.pkg',
21+
// Additional OS-architecture combinations can be added here
22+
}
23+
}
24+
25+
private async downloadFile(url: string, downloadDir: string) {
26+
const res = await fetch(url)
27+
// create dir if not exists
28+
29+
const file = await Deno.open(downloadDir, { create: true, write: true })
30+
31+
try {
32+
await res.body?.pipeTo(file.writable).finally(
33+
() => file.close(), //TODO: fix bad resource id when closing file
34+
)
35+
} catch (error) {
36+
console.error(`ERROR: [downloadFile] ${error.message}`)
37+
}
38+
39+
console.log('Closed file')
40+
}
41+
42+
private getUrl(): string {
43+
const key = `${this.os}`
44+
const url = this.urlMap[key]
45+
46+
if (!url) {
47+
throw new Error(`Unsupported OS type: ${this.os}`)
48+
}
49+
50+
return url
51+
}
52+
53+
/**
54+
* Gets binary name
55+
* @returns binrary name
56+
*/
57+
private getBinaryName() {
58+
const binaryMap: Record<SupportedOs, string> = {
59+
[SupportedOs.Windows]: 'stackql.exe',
60+
[SupportedOs.Darwin]: 'stackql/Payload/stackql',
61+
[SupportedOs.Linux]: 'stackql', // Default case for Linux and other platforms
62+
}
63+
const os = Deno.build.os.toLowerCase()
64+
65+
if (!Object.values(SupportedOs).includes(os as SupportedOs)) {
66+
throw new Error(`Unsupported OS type: ${os}`)
67+
}
68+
69+
const binaryOs = os as SupportedOs
70+
return binaryMap[binaryOs]
71+
}
72+
73+
private async createDownloadDir(downloadDir: string) {
74+
try {
75+
const stat = await Deno.stat(downloadDir)
76+
if (!stat.isDirectory) {
77+
await Deno.mkdir(downloadDir, { recursive: true })
78+
}
79+
} catch (error) {
80+
if (error instanceof Deno.errors.NotFound) {
81+
await Deno.mkdir(downloadDir, { recursive: true })
82+
} else {
83+
throw error
84+
}
85+
}
86+
}
87+
/**
88+
* Gets download dir
89+
* @returns download dir
90+
*/
91+
private getDownloadDir(): string {
92+
const projectDir = Deno.cwd()
93+
94+
if (!projectDir) {
95+
throw new Error('Unable to determine the project directory.')
96+
}
97+
98+
const downloadDir = join(projectDir, '.stackql')
99+
100+
return downloadDir
101+
}
102+
103+
private binaryExists(binaryName: string, downloadDir: string): boolean {
104+
const binPath = join(downloadDir, binaryName)
105+
try {
106+
Deno.statSync(binPath)
107+
return true
108+
} catch (error) {
109+
if (error instanceof Deno.errors.NotFound) {
110+
return false
111+
}
112+
throw error
113+
}
114+
}
115+
private async installStackQL(downloadDir: string) {
116+
const url = this.getUrl()
117+
118+
const archiveFileName = `${downloadDir}/${url.split('/').pop()}`
119+
await this.downloadFile(url, archiveFileName)
120+
121+
console.log('Unpacking stackql binary')
122+
const unpacker = Deno.build.os === 'darwin' ? darwinUnpack : unzip
123+
await unpacker({ downloadDir, archiveFileName })
124+
}
125+
126+
private async setExecutable(binaryPath: string) {
127+
const allowExecOctal = 0o755
128+
await osUtils.chomod(binaryPath, allowExecOctal)
129+
}
130+
131+
private async downloadAndInstallStackQL({
132+
downloadDir,
133+
binaryName,
134+
}: {
135+
downloadDir: string
136+
binaryName: string
137+
}) {
138+
const binaryPath = join(downloadDir, binaryName)
139+
await this.installStackQL(downloadDir)
140+
await this.setExecutable(binaryPath)
141+
return binaryPath
142+
}
143+
/**
144+
* Setup stackql binary, check if binary exists, if not download it
145+
*/
146+
public async setupStackQL() {
147+
try {
148+
const binaryName = this.getBinaryName()
149+
const downloadDir = this.getDownloadDir()
150+
await this.createDownloadDir(downloadDir)
151+
152+
let binaryPath = join(downloadDir, binaryName)
153+
154+
if (this.binaryExists(binaryName, downloadDir)) {
155+
await this.setExecutable(binaryPath)
156+
return binaryPath
157+
}
158+
159+
binaryPath = await this.downloadAndInstallStackQL({
160+
downloadDir,
161+
binaryName,
162+
})
163+
return binaryPath
164+
} catch (error) {
165+
console.error(`ERROR: [setup] ${error.message}`)
166+
Deno.exit(1)
167+
}
168+
}
169+
170+
private async removeStackQL() {
171+
const downloadDir = this.getDownloadDir()
172+
await Deno.remove(join(downloadDir, '/'), { recursive: true })
173+
console.log('stackql download dir removed')
174+
}
175+
176+
public async upgradeStackQL() {
177+
if (Deno.build.os === 'darwin') {
178+
await this.removeStackQL()
179+
}
180+
const binaryName = this.getBinaryName()
181+
const downloadDir = this.getDownloadDir()
182+
await this.createDownloadDir(downloadDir)
183+
const binaryPath = await this.downloadAndInstallStackQL({
184+
downloadDir,
185+
binaryName,
186+
})
187+
return binaryPath
188+
}
189+
}

0 commit comments

Comments
 (0)