Skip to content

Commit b7a75a6

Browse files
committed
fix
1 parent 81f3009 commit b7a75a6

File tree

4 files changed

+194
-5
lines changed

4 files changed

+194
-5
lines changed

.github/workflows/release.yaml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,22 @@ jobs:
9292
7z a "$DIRECTORY.zip" "$DIRECTORY"
9393
echo "ASSET=$DIRECTORY.zip" >> $GITHUB_ENV
9494
95+
- name: Generate SHA-256 checksum
96+
shell: bash
97+
run: |
98+
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
99+
certutil -hashfile "${{ env.ASSET }}" SHA256 | findstr /v "hash" | findstr /v "CertUtil" > "${{ env.ASSET }}.sha256"
100+
else
101+
shasum -a 256 "${{ env.ASSET }}" | awk '{print $1}' > "${{ env.ASSET }}.sha256"
102+
fi
103+
cat "${{ env.ASSET }}.sha256"
104+
95105
- name: Upload release archive
96106
uses: softprops/action-gh-release@v1
97107
with:
98108
files: |
99-
${{ env.ASSET }}
109+
${{ env.ASSET }}
110+
${{ env.ASSET }}.sha256
100111
101112
node:
102113
needs: assets

node/package-lock.json

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

node/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
"test": "npx jest",
1818
"prepublishOnly": "cp ../README.md . && npm i && npm run compile"
1919
},
20+
"dependencies": {
21+
"adm-zip": "^0.5.16"
22+
},
2023
"devDependencies": {
2124
"@types/jest": "^27.4.1",
2225
"@types/node": "^20.14.8",

node/postinstall.js

Lines changed: 167 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,171 @@
1-
let execSync = require('child_process').execSync
2-
let tag = require('./package.json').version
1+
const { execSync } = require('child_process')
2+
const { createHash } = require('crypto')
3+
const fs = require('fs')
4+
const https = require('https')
5+
const path = require('path')
6+
const { promisify } = require('util')
7+
const { pipeline } = require('stream')
8+
const streamPipeline = promisify(pipeline)
39

10+
const tag = require('./package.json').version
411
const os = process.platform
512
const cpu = process.arch
613

7-
execSync(`curl -LSfs https://jasonshin.github.io/sqlx-ts/install.sh | sh -s -- --os ${os} --cpu ${cpu} --tag ${tag} -f`, { stdio: 'inherit' })
8-
console.info('sqlx-ts installation successful')
14+
// Map Node.js platform and architecture to binary names
15+
function getBinaryInfo() {
16+
const buildMap = {
17+
'linux-ia32': 'linux-32-bit',
18+
'linux-x32': 'linux-32-bit',
19+
'linux-x64': 'linux-64-bit',
20+
'linux-arm64': 'linux-arm',
21+
'darwin-x64': 'macos-64-bit',
22+
'darwin-arm64': 'macos-arm',
23+
'win32-ia32': 'windows-32-bit',
24+
'win32-x32': 'windows-32-bit',
25+
'win32-x64': 'windows-64-bit',
26+
}
27+
28+
const key = `${os}-${cpu}`
29+
const build = buildMap[key]
30+
31+
if (!build) {
32+
throw new Error(`Unsupported platform: ${os}-${cpu}`)
33+
}
34+
35+
return {
36+
build,
37+
filename: `sqlx-ts-v${tag}-${build}.zip`,
38+
binaryName: os === 'win32' ? 'sqlx-ts.exe' : 'sqlx-ts'
39+
}
40+
}
41+
42+
// Download file from URL
43+
function downloadFile(url, destination) {
44+
return new Promise((resolve, reject) => {
45+
const file = fs.createWriteStream(destination)
46+
https.get(url, (response) => {
47+
if (response.statusCode === 302 || response.statusCode === 301) {
48+
// Handle redirect
49+
file.close()
50+
fs.unlinkSync(destination)
51+
return downloadFile(response.headers.location, destination)
52+
.then(resolve)
53+
.catch(reject)
54+
}
55+
56+
if (response.statusCode !== 200) {
57+
file.close()
58+
fs.unlinkSync(destination)
59+
return reject(new Error(`Failed to download: ${response.statusCode} ${response.statusMessage}`))
60+
}
61+
62+
response.pipe(file)
63+
file.on('finish', () => {
64+
file.close(resolve)
65+
})
66+
}).on('error', (err) => {
67+
fs.unlinkSync(destination)
68+
reject(err)
69+
})
70+
})
71+
}
72+
73+
// Calculate SHA-256 hash of a file
74+
function calculateSHA256(filePath) {
75+
return new Promise((resolve, reject) => {
76+
const hash = createHash('sha256')
77+
const stream = fs.createReadStream(filePath)
78+
79+
stream.on('data', (data) => hash.update(data))
80+
stream.on('end', () => resolve(hash.digest('hex')))
81+
stream.on('error', reject)
82+
})
83+
}
84+
85+
// Verify file hash
86+
async function verifyHash(filePath, expectedHash) {
87+
const actualHash = await calculateSHA256(filePath)
88+
89+
if (actualHash !== expectedHash) {
90+
throw new Error(
91+
`Hash mismatch!\n` +
92+
`Expected: ${expectedHash}\n` +
93+
`Got: ${actualHash}\n` +
94+
`This could indicate a corrupted download or a security issue.`
95+
)
96+
}
97+
98+
return true
99+
}
100+
101+
// Extract binary from zip
102+
function extractBinary(zipPath, binaryName, targetPath) {
103+
const AdmZip = require('adm-zip')
104+
const zip = new AdmZip(zipPath)
105+
const zipEntries = zip.getEntries()
106+
107+
for (const entry of zipEntries) {
108+
if (entry.entryName.endsWith(binaryName)) {
109+
// Extract the entry's content directly
110+
const data = entry.getData()
111+
fs.writeFileSync(targetPath, data)
112+
fs.chmodSync(targetPath, 0o755)
113+
return
114+
}
115+
}
116+
117+
throw new Error(`Binary ${binaryName} not found in archive`)
118+
}
119+
120+
async function install() {
121+
try {
122+
const { build, filename, binaryName } = getBinaryInfo()
123+
const baseUrl = `https://github.com/JasonShin/sqlx-ts/releases/download/v${tag}`
124+
const zipUrl = `${baseUrl}/${filename}`
125+
const checksumUrl = `${zipUrl}.sha256`
126+
127+
const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'sqlx-ts-'))
128+
const zipPath = path.join(tmpDir, filename)
129+
const checksumPath = path.join(tmpDir, `${filename}.sha256`)
130+
const targetPath = path.join(__dirname, 'sqlx-ts' + (os === 'win32' ? '.exe' : ''))
131+
132+
console.info(`Downloading sqlx-ts v${tag} for ${os}-${cpu}...`)
133+
console.info(`URL: ${zipUrl}`)
134+
135+
// Download the zip file
136+
await downloadFile(zipUrl, zipPath)
137+
console.info('Download complete')
138+
139+
// Download and verify the checksum
140+
try {
141+
console.info('Downloading checksum...')
142+
await downloadFile(checksumUrl, checksumPath)
143+
const expectedHash = fs.readFileSync(checksumPath, 'utf8').trim()
144+
console.info(`Expected SHA-256: ${expectedHash}`)
145+
146+
// Verify the hash
147+
console.info('Verifying checksum...')
148+
await verifyHash(zipPath, expectedHash)
149+
console.info('Checksum verified successfully')
150+
} catch (error) {
151+
console.warn('Warning: Could not download or verify checksum.')
152+
console.warn('This is expected for releases before SHA-256 checksums were added.')
153+
console.warn('Proceeding without verification (not recommended for production).')
154+
console.warn(`Checksum URL: ${checksumUrl}`)
155+
}
156+
157+
// Extract the binary
158+
console.info('Extracting binary...')
159+
extractBinary(zipPath, binaryName, targetPath)
160+
161+
// Cleanup
162+
fs.rmSync(tmpDir, { recursive: true, force: true })
163+
164+
console.info('sqlx-ts installation successful')
165+
} catch (error) {
166+
console.error('Installation failed:', error.message)
167+
process.exit(1)
168+
}
169+
}
170+
171+
install()

0 commit comments

Comments
 (0)