Compare commits
5 commits
b4310c09a8
...
07d276b2a3
Author | SHA1 | Date | |
---|---|---|---|
07d276b2a3 | |||
17560621e0 | |||
7fe4b8c08e | |||
bfdd518233 | |||
7f3bc81bd2 |
12 changed files with 99 additions and 60 deletions
26
cli.go
Normal file
26
cli.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"gitea.nulo.in/Nulo/repro-run/runner"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
config, err := readConfig()
|
||||||
|
must(err)
|
||||||
|
must(runner.Run(config, "rootfs/", "cache/"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func must(err error, where ...string) {
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err, where)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readConfig() (config runner.Config, err error) {
|
||||||
|
err = json.NewDecoder(os.Stdin).Decode(&config)
|
||||||
|
return
|
||||||
|
}
|
16
config.go
16
config.go
|
@ -1,16 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Command string
|
|
||||||
Cache []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func readConfig() (config Config, err error) {
|
|
||||||
err = json.NewDecoder(os.Stdin).Decode(&config)
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -17,8 +17,7 @@ file repro-run
|
||||||
```
|
```
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
echo '{"Command": "/src/test", "Cache": ["/home/repro/.cache/go-build"]}
|
echo '{"Command": "/src/test", "Cache": ["/home/repro/.cache/go-build"]}' | go run .
|
||||||
' | go run .
|
|
||||||
```
|
```
|
||||||
|
|
||||||
genera
|
genera
|
||||||
|
|
5
repro-build
Executable file
5
repro-build
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/sh -xe
|
||||||
|
apk add --quiet git go
|
||||||
|
git clone --quiet file:///src repo
|
||||||
|
cd repo
|
||||||
|
go build .
|
1
repro-run.json
Normal file
1
repro-run.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{ "Command": "/src/repro-build", "Cache": ["/home/repro/.cache/go-build"] }
|
6
runner/config.go
Normal file
6
runner/config.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package runner
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Command string
|
||||||
|
Cache []string
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
package main
|
package runner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -13,53 +14,71 @@ import (
|
||||||
//go:embed alpine/keys
|
//go:embed alpine/keys
|
||||||
var alpineKeys embed.FS
|
var alpineKeys embed.FS
|
||||||
|
|
||||||
func main() {
|
func Run(config Config, rootfs string, cache string) (err error) {
|
||||||
config, err := readConfig()
|
if err = os.RemoveAll("rootfs/"); err != nil {
|
||||||
must(err)
|
return
|
||||||
|
|
||||||
must(os.RemoveAll("rootfs/"))
|
|
||||||
|
|
||||||
must(os.MkdirAll("rootfs/etc/apk/keys", 0700))
|
|
||||||
must(os.WriteFile("rootfs/etc/apk/repositories", []byte(
|
|
||||||
`https://dl-cdn.alpinelinux.org/alpine/v3.17/main
|
|
||||||
https://dl-cdn.alpinelinux.org/alpine/v3.17/community`), 0600))
|
|
||||||
must(os.WriteFile("rootfs/etc/resolv.conf", []byte(
|
|
||||||
`nameserver 8.8.8.8`), 0644))
|
|
||||||
keys, err := alpineKeys.ReadDir("alpine/keys")
|
|
||||||
must(err)
|
|
||||||
for _, entry := range keys {
|
|
||||||
o, err := os.Create(path.Join("rootfs/etc/apk/keys/", entry.Name()))
|
|
||||||
must(err)
|
|
||||||
s, err := alpineKeys.Open(path.Join("alpine/keys", entry.Name()))
|
|
||||||
must(err)
|
|
||||||
_, err = io.Copy(o, s)
|
|
||||||
must(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
must(os.MkdirAll("cache/_var_cache_apk", 0700))
|
if err = os.MkdirAll("rootfs/etc/apk/keys", 0700); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = os.WriteFile("rootfs/etc/apk/repositories", []byte(
|
||||||
|
`https://dl-cdn.alpinelinux.org/alpine/v3.17/main
|
||||||
|
https://dl-cdn.alpinelinux.org/alpine/v3.17/community`), 0600); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.WriteFile("rootfs/etc/resolv.conf", []byte(`nameserver 8.8.8.8`), 0644); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
keys, err := alpineKeys.ReadDir("alpine/keys")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, entry := range keys {
|
||||||
|
o, err := os.Create(path.Join("rootfs/etc/apk/keys/", entry.Name()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s, err := alpineKeys.Open(path.Join("alpine/keys", entry.Name()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = io.Copy(o, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.MkdirAll("cache/_var_cache_apk", 0700); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
os.Remove("rootfs/etc/apk/cache")
|
os.Remove("rootfs/etc/apk/cache")
|
||||||
must(os.Symlink("/var/cache/apk", "rootfs/etc/apk/cache"))
|
if err = os.Symlink("/var/cache/apk", "rootfs/etc/apk/cache"); err != nil {
|
||||||
// must(os.MkdirAll("rootfs/var/cache", 0700))
|
return
|
||||||
// must(os.RemoveAll("rootfs/var/cache/apk"))
|
}
|
||||||
// must(os.Symlink("../../../cache/apk", "rootfs/var/cache/apk"))
|
|
||||||
cmd := exec.Command("apk", "add",
|
cmd := exec.Command("apk", "add",
|
||||||
"--root", "rootfs",
|
"--root", "rootfs",
|
||||||
"--initdb",
|
"--initdb",
|
||||||
"--cache-dir", "../cache/_var_cache_apk",
|
"--cache-dir", "../cache/_var_cache_apk",
|
||||||
"apk-tools")
|
"apk-tools")
|
||||||
must(cmd.Run(), "apk add apk-tools")
|
if err = cmd.Run(); err != nil {
|
||||||
// must(os.Remove("rootfs/var/cache/apk"))
|
return fmt.Errorf("apk add apk-tools: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
cached := append(config.Cache, "/var/cache/apk")
|
cached := append(config.Cache, "/var/cache/apk")
|
||||||
var cachedParams []string
|
var cachedParams []string
|
||||||
for _, c := range cached {
|
for _, c := range cached {
|
||||||
cacheDir := path.Join("./cache", strings.ReplaceAll(c, "/", "_"))
|
cacheDir := path.Join("./cache", strings.ReplaceAll(c, "/", "_"))
|
||||||
must(os.MkdirAll(cacheDir, 0700))
|
if err = os.MkdirAll(cacheDir, 0700); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
cachedParams = append(cachedParams, "--bind", cacheDir, c)
|
cachedParams = append(cachedParams, "--bind", cacheDir, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp, err := os.MkdirTemp("", "repro-run-")
|
tmp, err := os.MkdirTemp("", "repro-run-")
|
||||||
must(err)
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
defer os.RemoveAll(tmp)
|
defer os.RemoveAll(tmp)
|
||||||
log.Println("/work = " + tmp)
|
log.Println("/work = " + tmp)
|
||||||
|
|
||||||
|
@ -78,24 +97,23 @@ https://dl-cdn.alpinelinux.org/alpine/v3.17/community`), 0600))
|
||||||
|
|
||||||
cmd = exec.Command("bwrap", append(params,
|
cmd = exec.Command("bwrap", append(params,
|
||||||
"apk", "add", "busybox", "busybox-suid", "libc-utils", "alpine-baselayout", "alpine-conf", "alpine-release")...)
|
"apk", "add", "busybox", "busybox-suid", "libc-utils", "alpine-baselayout", "alpine-conf", "alpine-release")...)
|
||||||
// cmd.Stdout = os.Stdout
|
if err = cmd.Run(); err != nil {
|
||||||
// cmd.Stderr = os.Stderr
|
return fmt.Errorf("bwrap apk add: %w", err)
|
||||||
must(cmd.Run(), "bwrap apk add")
|
}
|
||||||
cmd = exec.Command("bwrap", append(params,
|
cmd = exec.Command("bwrap", append(params,
|
||||||
"--uid", "0",
|
"--uid", "0",
|
||||||
"adduser", "-u", "1000", "-D", "repro")...)
|
"adduser", "-u", "1000", "-D", "repro")...)
|
||||||
must(cmd.Run(), "bwrap adduser repro")
|
if err = cmd.Run(); err != nil {
|
||||||
|
return fmt.Errorf("bwrap adduser repro: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
cmd = exec.Command("bwrap", append(params, strings.Split(config.Command, " ")...)...)
|
cmd = exec.Command("bwrap", append(params, strings.Split(config.Command, " ")...)...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stdin = os.Stdin
|
cmd.Stdin = os.Stdin
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
must(cmd.Run())
|
if err = cmd.Run(); err != nil {
|
||||||
|
return fmt.Errorf("running Command: %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
func must(err error, where ...string) {
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err, where)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
Loading…
Reference in a new issue