打包构建
This commit is contained in:
1 parent
a6e45e0cb1
commit
ee8772c1b7
36 files changed
+940
-21
No files matched your search
@@ -0,0 +1,42 @@
|
||||
import { createHash } from 'node:crypto'
|
||||
import { createReadStream } from 'node:fs'
|
||||
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const pkg = JSON.parse(await readFile(path.join(root, 'package.json'), 'utf8'))
|
||||
const repository = process.env.PICHOST_IMAGE_REPOSITORY || 'yeomaiyelove/pichost'
|
||||
const image = `${repository}:${pkg.version}`
|
||||
const platform = process.env.DOCKER_PLATFORM || 'linux/amd64'
|
||||
const outputDir = path.join(root, 'dist-docker')
|
||||
const outputName = `${repository.replaceAll('/', '-')}-${pkg.version}-amd64.tar`
|
||||
const outputFile = path.join(outputDir, outputName)
|
||||
|
||||
function run(command, args, cwd = root) {
|
||||
const result = spawnSync(command, args, {
|
||||
cwd,
|
||||
stdio: 'inherit',
|
||||
windowsHide: true
|
||||
})
|
||||
if (result.error) throw result.error
|
||||
if (result.status !== 0) {
|
||||
throw new Error(`${command} ${args.join(' ')} 失败,退出代码 ${result.status}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function sha256(file) {
|
||||
const hash = createHash('sha256')
|
||||
for await (const chunk of createReadStream(file)) hash.update(chunk)
|
||||
return hash.digest('hex')
|
||||
}
|
||||
|
||||
run('docker', ['build', '--platform', platform, '-t', image, '.'])
|
||||
await mkdir(outputDir, { recursive: true })
|
||||
run('docker', ['save', '-o', outputFile, image])
|
||||
|
||||
const digest = await sha256(outputFile)
|
||||
await writeFile(`${outputFile}.sha256`, `${digest} ${outputName}\n`)
|
||||
console.log(`Docker 镜像包:${outputFile}`)
|
||||
console.log(`SHA256:${digest}`)
|
||||
@@ -0,0 +1,130 @@
|
||||
import { createHash } from 'node:crypto'
|
||||
import {
|
||||
access,
|
||||
chmod,
|
||||
cp,
|
||||
mkdir,
|
||||
readFile,
|
||||
readdir,
|
||||
rename,
|
||||
rm,
|
||||
writeFile
|
||||
} from 'node:fs/promises'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import sharp from 'sharp'
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const pkg = JSON.parse(await readFile(path.join(root, 'package.json'), 'utf8'))
|
||||
const outputRoot = path.join(root, 'dist-fpk')
|
||||
const stage = path.join(outputRoot, 'staging')
|
||||
const source = path.join(root, 'packaging', 'fnos')
|
||||
const cache = path.join(outputRoot, 'cache')
|
||||
const image = process.env.PICHOST_IMAGE
|
||||
|| `yeomaiyelove/pichost:${pkg.version}`
|
||||
const platform = process.env.FPK_PLATFORM || 'x86'
|
||||
const fnpackVersion = '1.2.3'
|
||||
|
||||
function run(command, args, cwd = root) {
|
||||
const result = spawnSync(command, args, {
|
||||
cwd,
|
||||
stdio: 'inherit',
|
||||
windowsHide: true
|
||||
})
|
||||
if (result.error) throw result.error
|
||||
if (result.status !== 0) {
|
||||
throw new Error(`${command} ${args.join(' ')} 失败,退出代码 ${result.status}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function exists(file) {
|
||||
try {
|
||||
await access(file)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function getFnpack() {
|
||||
const systems = {
|
||||
win32: 'windows',
|
||||
linux: 'linux',
|
||||
darwin: 'darwin'
|
||||
}
|
||||
const architectures = {
|
||||
x64: 'amd64',
|
||||
arm64: 'arm64'
|
||||
}
|
||||
const system = systems[process.platform]
|
||||
const architecture = architectures[process.arch]
|
||||
if (!system || !architecture) {
|
||||
throw new Error(`不支持自动下载 fnpack:${process.platform}/${process.arch}`)
|
||||
}
|
||||
|
||||
const executable = path.join(
|
||||
cache,
|
||||
`fnpack-${fnpackVersion}-${system}-${architecture}${process.platform === 'win32' ? '.exe' : ''}`
|
||||
)
|
||||
if (!await exists(executable)) {
|
||||
await mkdir(cache, { recursive: true })
|
||||
const url = `https://static2.fnnas.com/fnpack/fnpack-${fnpackVersion}-${system}-${architecture}`
|
||||
console.log(`下载 fnpack:${url}`)
|
||||
const response = await fetch(url)
|
||||
if (!response.ok) throw new Error(`fnpack 下载失败:${response.status}`)
|
||||
await writeFile(executable, Buffer.from(await response.arrayBuffer()))
|
||||
if (process.platform !== 'win32') await chmod(executable, 0o755)
|
||||
}
|
||||
return executable
|
||||
}
|
||||
|
||||
async function replace(file, replacer) {
|
||||
const original = await readFile(file, 'utf8')
|
||||
await writeFile(file, replacer(original))
|
||||
}
|
||||
|
||||
await rm(stage, { recursive: true, force: true })
|
||||
await cp(source, stage, { recursive: true })
|
||||
|
||||
const cmdDir = path.join(stage, 'cmd')
|
||||
for (const file of await readdir(cmdDir)) {
|
||||
await chmod(path.join(cmdDir, file), 0o755)
|
||||
}
|
||||
await chmod(path.join(stage, 'app', 'docker', 'start.sh'), 0o755)
|
||||
|
||||
await replace(path.join(stage, 'manifest'), text => text
|
||||
.replace(/^version\s*=.*$/m, `version = ${pkg.version}`)
|
||||
.replace(/^platform\s*=.*$/m, `platform = ${platform}`))
|
||||
await replace(path.join(stage, 'app', 'docker', 'docker-compose.yaml'), text => text
|
||||
.replace(/image:\s*\S+/, `image: ${image}`))
|
||||
await replace(path.join(stage, 'wizard', 'install'), text => text
|
||||
.replaceAll(/yeomaiyelove\/pichost:[0-9]+\.[0-9]+\.[0-9]+/g, image))
|
||||
|
||||
const logo = path.join(root, 'app', 'assets', 'image', 'logo-light.png')
|
||||
const resize = size => sharp(logo)
|
||||
.resize(size, size, {
|
||||
fit: 'contain',
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
||||
})
|
||||
.png()
|
||||
|
||||
await mkdir(path.join(stage, 'app', 'ui', 'images'), { recursive: true })
|
||||
await resize(64).toFile(path.join(stage, 'ICON.PNG'))
|
||||
await resize(256).toFile(path.join(stage, 'ICON_256.PNG'))
|
||||
await resize(64).toFile(path.join(stage, 'app', 'ui', 'images', 'icon_64.png'))
|
||||
await resize(256).toFile(path.join(stage, 'app', 'ui', 'images', 'icon_256.png'))
|
||||
|
||||
const fnpack = await getFnpack()
|
||||
run(fnpack, ['build', '--directory', stage], outputRoot)
|
||||
|
||||
const output = path.join(outputRoot, `pichost-${pkg.version}.fpk`)
|
||||
await rm(output, { force: true })
|
||||
await rename(path.join(outputRoot, 'pichost.fpk'), output)
|
||||
|
||||
const hash = createHash('sha256')
|
||||
hash.update(await readFile(output))
|
||||
const digest = hash.digest('hex')
|
||||
await writeFile(`${output}.sha256`, `${digest} ${path.basename(output)}\n`)
|
||||
console.log(`FPK:${output}`)
|
||||
console.log(`SHA256:${digest}`)
|
||||
Reference in new issue
Block a user