diff --git a/.gitignore b/.gitignore index ba8d7b0..6a79c3e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ *.img *.model *.manifest +node_modules +package-lock.json +snaps diff --git a/Makefile b/Makefile index 52b644f..99f0cea 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,14 @@ CHANNEL=stable +ENV=dev clean: rm -rf *.img *.model *.manifest %.model: boards/%.yaml # sign a model file cat definition.yaml $< | python -c "import sys, yaml, json; json.dump(yaml.load(sys.stdin), sys.stdout, indent=2)" | sed "s|TIMESTAMP|$(shell date -Iseconds --utc)|g" | snap sign -k default > $@ -%.img: %.model # build an image +snaps/%: + node build-tool.js --env $(ENV) --channel $(CHANNEL) --board $* +%.img: %.model snaps/% # build an image sudo ubuntu-image snap -o $@ -c $(CHANNEL) $< # VM stuff @@ -16,4 +19,3 @@ start: vm.img # launch the image with kvm kvm -smp 2 -m 1500 -netdev user,id=mynet0,hostfwd=tcp::8022-:22,hostfwd=tcp::8090-:80 -device virtio-net-pci,netdev=mynet0 -drive file=vm.img,format=raw ssh: # ssh into it (don't check the key because that one changes after every rebuild) ssh mkg20001@localhost -p 8022 -o StrictHostKeyChecking=no -o GlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null - diff --git a/boards/opi0.yaml b/boards/opi0.yaml new file mode 100644 index 0000000..510d1b0 --- /dev/null +++ b/boards/opi0.yaml @@ -0,0 +1,3 @@ +architecture: armhf +gadget: orangepi-zero-gadget +kernel: orangepi-zero-kernel diff --git a/build-tool.js b/build-tool.js new file mode 100644 index 0000000..018be25 --- /dev/null +++ b/build-tool.js @@ -0,0 +1,86 @@ +#!/usr/bin/env node + +/* eslint-disable no-console */ + +'use strict' + +const {board, env} = require('yargs').argv +require('colors') +const fs = require('fs') +const path = require('path') +const read = (...file) => String(fs.readFileSync(path.join(__dirname, ...file))) +const yaml = require('js-yaml') +const cp = require('child_process') +const JSON5 = require('json5') +const mkdir = require('mkdirp').sync + +const isDev = env === 'dev' +const forceBuild = isDev + +const deps = yaml.safeLoad(read('deps/' + board + '.yaml')) +const boardDef = yaml.safeLoad(read('boards/' + board + '.yaml')) + +const exec = (cmd, dir, ...args) => new Promise((resolve, reject) => { + console.log('$ %s'.bold, [cmd, ...args].map(JSON5.stringify).join(' ')) + const p = cp.spawn(cmd, args, {stdio: 'inherit', cwd: dir}) + p.once('close', (code, sig) => { + if (code || sig) { + return reject(new Error('Failed with code/sig: ' + (code || sig))) + } + + resolve() + }) +}) + +const execWithOutput = (cmd, ...args) => { + const p = cp.spawnSync(cmd, args) + if (p.status || p.signal) { + throw new Error('Failed with code/sig: ' + (p.status || p.signal)) + } + + return p +} + +async function snapcraft (folder, outFile, targetArch) { + await exec('snapcraft', folder, 'snap', '-o', outFile, '--target-arch=' + targetArch) +} + +async function pullOrClone (repo, dest, checkout) { + let currentCommit = '' + if (fs.existsSync(dest)) { + await exec('git', dest, 'remote', 'update', '--recurse-submodules=yes', '-p') + } else { + await exec('git', process.cwd(), 'clone', '--recursive', repo, dest) + currentCommit = String(execWithOutput('git', '-C', dest, 'rev-parse', '--verify', 'HEAD').stdout) + } + await exec('git', dest, 'checkout', checkout) + let newCommit = String(execWithOutput('git', '-C', dest, 'rev-parse', '--verify', 'HEAD').stdout) + + return currentCommit !== newCommit // returns bool if snap need recompilation +} + +async function main () { + for (const snapName in deps.snaps) { // eslint-disable-line guard-for-in + const snap = deps.snaps[snapName] + if (snap.mustBuild || forceBuild) { + console.log('Building %s...', snapName) + + let buildFolder = path.join('snaps', board, snapName) + mkdir(buildFolder) + + let sourceFolder = path.join(buildFolder, 'source') + let snapFile = path.join(buildFolder, snapName + '.snap') + if (await pullOrClone(snap.git, path.join(buildFolder, 'source'), snap.version)) { + if (fs.existsSync(snapFile)) { + fs.unlinkSync(snapFile) + } + } + + if (!fs.existsSync(snapFile)) { + await snapcraft(sourceFolder, snapFile, boardDef.architecture) + } + } + } +} + +main() diff --git a/definition.yaml b/definition.yaml index c0da4c2..57f0881 100644 --- a/definition.yaml +++ b/definition.yaml @@ -1,7 +1,7 @@ # basic type: model series: "16" -model: hub +model: ppl-hub # snaps to be preinstalled: required-snaps: - nextcloud # just for proof-of-concept diff --git a/deps/opi0.yaml b/deps/opi0.yaml new file mode 100644 index 0000000..74b3bd2 --- /dev/null +++ b/deps/opi0.yaml @@ -0,0 +1,9 @@ +snaps: + orangepi-zero-gadget: + git: https://github.com/fuzeman/orangepi-zero-gadget + version: 1.0.0-beta.3 + mustBuild: true # snap is only available in orange pi store + orangepi-zero-kernel: + git: https://github.com/fuzeman/orangepi-zero-kernel + version: 4.10.0-2001.2-beta.2 + mustBuild: true # snap is only available in orange pi store diff --git a/package.json b/package.json new file mode 100644 index 0000000..f69f367 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "ppl-os", + "version": "0.0.1", + "description": "Files required to build the ubuntu core based pplOS", + "main": "build-tool.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "ssh://gitea@git.coolaj86.com:22042/mkg20001/ppl-os.git" + }, + "keywords": [ + "pplOS", + "pplfamily", + "ubuntu-core", + "build-tool" + ], + "author": "Maciej Krüger ", + "license": "MIT", + "dependencies": { + "colors": "^1.3.1", + "js-yaml": "^3.12.0", + "json5": "^1.0.1", + "mkdirp": "^0.5.1", + "yargs": "^12.0.1" + } +}