Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1071058f4e | ||
|
|
d2a0510cb4 | ||
|
|
583f65040a |
@@ -1,9 +1,9 @@
|
|||||||
name: check
|
name: check
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main, next]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [main, next]
|
||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -20,21 +20,33 @@ jobs:
|
|||||||
# check.yml runs script/cibuild, which does all of its work inside
|
# check.yml runs script/cibuild, which does all of its work inside
|
||||||
# the digest-pinned Dockerfile images -- so without this step the
|
# the digest-pinned Dockerfile images -- so without this step the
|
||||||
# release either fails at the before-hook or, worse, ships binaries
|
# release either fails at the before-hook or, worse, ships binaries
|
||||||
# built by whatever Go the runner happens to carry.
|
# built by whatever unpinned Go the runner happens to carry.
|
||||||
|
# REPO_POLICIES.md requires every external reference to be pinned,
|
||||||
|
# and script/release already refuses a goreleaser that is not the
|
||||||
|
# pinned build; the compiler that actually produces the artifacts
|
||||||
|
# is the last thing that should be exempt from that.
|
||||||
#
|
#
|
||||||
# actions/setup-go would pin the action by commit sha, but the Go
|
# go-version-file rather than a literal: go.mod's `go 1.26.1` is
|
||||||
# tarball it downloads at runtime is verified against no value in
|
# the single source of truth for the toolchain, the same way the
|
||||||
# this repo, and the action exposes no checksum input.
|
# Dockerfile FROM line is the single source of truth for the
|
||||||
# REPO_POLICIES.md requires every external reference to be pinned
|
# linter version that script/lint enforces. It is a three-component
|
||||||
# by hash with no exceptions, and this is the compiler that
|
# version, so setup-go resolves it exactly -- no silent drift onto
|
||||||
# produces the published binaries -- the input where a substituted
|
# a newer patch release.
|
||||||
# artifact matters most. So Go is installed the way goreleaser is:
|
#
|
||||||
# script/install-go downloads the exact archive for go.mod's `go`
|
# actions/setup-go v5.6.0, 2025-12-15. Pinned by commit sha, like
|
||||||
# directive and refuses it unless its sha256 matches the value
|
# the checkout above. v5.x is a node20 action, matching the node20
|
||||||
# committed in the script, then puts .tool/go/bin on PATH for the
|
# actions/checkout v4 already in use here; the v6/v7 line requires
|
||||||
# steps below.
|
# a node24 runner, which this Gitea runner has never been asked
|
||||||
|
# for and cannot be assumed to provide.
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
run: script/install-go
|
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
# setup-go's module cache needs a runner-side cache backend.
|
||||||
|
# A release is cut rarely and a cold module download costs
|
||||||
|
# seconds; a release failing because a cache service is absent
|
||||||
|
# costs a re-tag. Off, deliberately.
|
||||||
|
cache: false
|
||||||
- name: Install goreleaser
|
- name: Install goreleaser
|
||||||
run: script/install-goreleaser
|
run: script/install-goreleaser
|
||||||
- name: Release
|
- name: Release
|
||||||
@@ -46,8 +58,3 @@ jobs:
|
|||||||
# It is deliberately not the runner's automatic token, which is
|
# It is deliberately not the runner's automatic token, which is
|
||||||
# not guaranteed to carry that scope.
|
# not guaranteed to carry that scope.
|
||||||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
# Build with the toolchain install-go just verified, never a
|
|
||||||
# different one auto-downloaded from a `toolchain` directive:
|
|
||||||
# the point of the hash pin is that this exact compiler makes
|
|
||||||
# the release.
|
|
||||||
GOTOOLCHAIN: local
|
|
||||||
|
|||||||
@@ -649,14 +649,6 @@ them. We provide:
|
|||||||
called by `script/bootstrap`; the release workflow calls it directly
|
called by `script/bootstrap`; the release workflow calls it directly
|
||||||
because it needs `goreleaser` but not the Docker daemon
|
because it needs `goreleaser` but not the Docker daemon
|
||||||
`script/bootstrap` insists on.
|
`script/bootstrap` insists on.
|
||||||
* `script/install-go` — install the Go toolchain named by `go.mod`'s
|
|
||||||
`go` directive into `.tool/go` from a sha256-verified `go.dev`
|
|
||||||
archive, and put it on `PATH`. Idempotent. Called only by the release
|
|
||||||
workflow, which needs a host Go for `goreleaser` to shell out to;
|
|
||||||
nothing else on the release runner does. `actions/setup-go` is not
|
|
||||||
used because it verifies the downloaded toolchain against no value in
|
|
||||||
this repo. Bumping Go edits `go.mod`, the checksum in this script, and
|
|
||||||
the `Dockerfile` `golang` digest together.
|
|
||||||
* `script/release` — cross-compile and publish the release artifacts
|
* `script/release` — cross-compile and publish the release artifacts
|
||||||
with the pinned `goreleaser`. Refuses a `goreleaser` on `PATH` whose
|
with the pinned `goreleaser`. Refuses a `goreleaser` on `PATH` whose
|
||||||
version is not the pinned one, on the same reasoning as `script/lint`.
|
version is not the pinned one, on the same reasoning as `script/lint`.
|
||||||
@@ -724,6 +716,8 @@ them. We provide:
|
|||||||
then the product image). Either failing fails the script. It runs the
|
then the product image). Either failing fails the script. It runs the
|
||||||
checks in the same containers CI does, from a clean copy of the tree,
|
checks in the same containers CI does, from a clean copy of the tree,
|
||||||
so it also catches anything that depends on host state.
|
so it also catches anything that depends on host state.
|
||||||
|
`.gitea/workflows/check.yml` runs it on every push to `main` and
|
||||||
|
`next` and on every pull request against either.
|
||||||
|
|
||||||
It passes a fresh `--build-arg CHECK_EPOCH` to each build, unique per
|
It passes a fresh `--build-arg CHECK_EPOCH` to each build, unique per
|
||||||
invocation, which both files declare immediately above their check
|
invocation, which both files declare immediately above their check
|
||||||
|
|||||||
@@ -25,15 +25,11 @@ release" is exactly the contradiction
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
- 2026-09-21: Hash-verified the Go toolchain in the release workflow
|
- 2026-09-21: Made `.gitea/workflows/check.yml` run on pushes to `main`
|
||||||
([issue #105](https://git.eeqj.de/sneak/vaultik/issues/105)). New
|
and `next` and on pull requests against either, so unit PRs (whose
|
||||||
`script/install-go` downloads the exact `go.dev` archive for `go.mod`'s
|
base is `next`) and `next` itself get a CI run instead of relying on a
|
||||||
`go` directive and refuses it unless its sha256 matches a value
|
local `make check`
|
||||||
committed in the script; `.gitea/workflows/release.yml` calls it
|
([issue #122](https://git.eeqj.de/sneak/vaultik/issues/122)).
|
||||||
instead of `actions/setup-go`, which verified the downloaded toolchain
|
|
||||||
against nothing in the repo. `GOTOOLCHAIN: local` on the release step
|
|
||||||
keeps that exact compiler from auto-switching. Bumping Go now touches
|
|
||||||
`go.mod`, the checksum, and the `Dockerfile` `golang` digest together.
|
|
||||||
|
|
||||||
- 2026-08-10: Moved every lint run into its own container, as a build
|
- 2026-08-10: Moved every lint run into its own container, as a build
|
||||||
step ([issue #113](https://git.eeqj.de/sneak/vaultik/issues/113)).
|
step ([issue #113](https://git.eeqj.de/sneak/vaultik/issues/113)).
|
||||||
|
|||||||
+30
-1
@@ -1,6 +1,7 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -24,6 +25,11 @@ const configSetArgs = 2
|
|||||||
// parent config dirs (e.g. ~/.config) are conventionally traversable.
|
// parent config dirs (e.g. ~/.config) are conventionally traversable.
|
||||||
const configDirMode = 0o755
|
const configDirMode = 0o755
|
||||||
|
|
||||||
|
// configYAMLIndent matches the 2-space indentation of defaultConfigTemplate,
|
||||||
|
// so `config set` writes the file back with the same indentation rather than
|
||||||
|
// yaml.Marshal's 4-space default.
|
||||||
|
const configYAMLIndent = 2
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errConfigExists = errors.New("config file already exists")
|
errConfigExists = errors.New("config file already exists")
|
||||||
errEmptyConfig = errors.New("empty config file")
|
errEmptyConfig = errors.New("empty config file")
|
||||||
@@ -381,7 +387,7 @@ Examples:
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := yaml.Marshal(root)
|
out, err := marshalConfigYAML(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("marshaling config: %w", err)
|
return fmt.Errorf("marshaling config: %w", err)
|
||||||
}
|
}
|
||||||
@@ -405,6 +411,29 @@ Examples:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// marshalConfigYAML renders a config document tree with 2-space indentation,
|
||||||
|
// matching defaultConfigTemplate. yaml.Marshal defaults to 4 spaces, which
|
||||||
|
// would reindent the whole file on the first `config set` despite the promise
|
||||||
|
// to preserve formatting.
|
||||||
|
func marshalConfigYAML(root *yaml.Node) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
enc := yaml.NewEncoder(&buf)
|
||||||
|
enc.SetIndent(configYAMLIndent)
|
||||||
|
|
||||||
|
err := enc.Encode(root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = enc.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// loadYAMLFile parses a YAML file into a yaml.Node document tree,
|
// loadYAMLFile parses a YAML file into a yaml.Node document tree,
|
||||||
// which preserves comments and ordering for round-tripping.
|
// which preserves comments and ordering for round-tripping.
|
||||||
func loadYAMLFile(path string) (*yaml.Node, error) {
|
func loadYAMLFile(path string) (*yaml.Node, error) {
|
||||||
|
|||||||
@@ -188,6 +188,47 @@ func TestYAMLPathSet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestConfigSetPreservesFormatting asserts the `config set` write path
|
||||||
|
// (marshalConfigYAML) round-trips a 2-space-indented file without reindenting
|
||||||
|
// it to yaml.Marshal's 4-space default, and keeps comments.
|
||||||
|
func TestConfigSetPreservesFormatting(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
root := parseTestYAML(t)
|
||||||
|
|
||||||
|
err := yamlPathSet(root, splitPath("s3.bucket"), "newbucket")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("set s3.bucket: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := marshalConfigYAML(root)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("marshal: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
text := string(out)
|
||||||
|
|
||||||
|
for _, want := range []string{"# top comment", "# inline comment"} {
|
||||||
|
if !contains(text, want) {
|
||||||
|
t.Errorf("round-tripped YAML dropped comment %q:\n%s", want, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nested map keys stay at 2-space indent; the bug reindented them to 4.
|
||||||
|
if !contains(text, "\n bucket: newbucket") {
|
||||||
|
t.Errorf("expected 2-space indent for s3.bucket, got:\n%s", text)
|
||||||
|
}
|
||||||
|
|
||||||
|
if contains(text, "\n bucket:") {
|
||||||
|
t.Errorf("s3.bucket reindented to 4 spaces:\n%s", text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sequence items under a key also stay at 2 spaces.
|
||||||
|
if !contains(text, "\n - age1aaa") {
|
||||||
|
t.Errorf("expected 2-space indent for sequence item, got:\n%s", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func splitPath(s string) []string {
|
func splitPath(s string) []string {
|
||||||
return strings.Split(s, ".")
|
return strings.Split(s, ".")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ specifying a path using --config or by setting VAULTIK_CONFIG to a path.`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flags().BoolVar(&opts.Cron, "cron", false,
|
cmd.Flags().BoolVar(&opts.Cron, "cron", false,
|
||||||
"Run in cron mode (silent unless error)")
|
"Run in cron mode (silent unless warning or error)")
|
||||||
cmd.Flags().BoolVar(&opts.Prune, "prune", false,
|
cmd.Flags().BoolVar(&opts.Prune, "prune", false,
|
||||||
"After backup, drop older snapshots of the same name and remove "+
|
"After backup, drop older snapshots of the same name and remove "+
|
||||||
"orphaned blobs")
|
"orphaned blobs")
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
package cli
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// SnapshotInfo represents snapshot information for listing
|
|
||||||
//
|
|
||||||
//nolint:tagliatelle // snake_case is the established output format
|
|
||||||
type SnapshotInfo struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Timestamp time.Time `json:"timestamp"`
|
|
||||||
CompressedSize int64 `json:"compressed_size"`
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
// Package models defines shared value types describing files, chunks,
|
|
||||||
// blobs, and snapshots as they move through the backup pipeline.
|
|
||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FileInfo represents a file in the backup system
|
|
||||||
type FileInfo struct {
|
|
||||||
Path string
|
|
||||||
MTime time.Time
|
|
||||||
Size int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChunkInfo represents a content-addressed chunk
|
|
||||||
type ChunkInfo struct {
|
|
||||||
Hash string // SHA256 hash
|
|
||||||
Size int64
|
|
||||||
Offset int64 // Offset within source file
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChunkRef represents a reference to a chunk in a blob or file
|
|
||||||
type ChunkRef struct {
|
|
||||||
ChunkHash string
|
|
||||||
Offset int64
|
|
||||||
Length int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlobInfo represents an encrypted blob containing multiple chunks
|
|
||||||
type BlobInfo struct {
|
|
||||||
Hash string // SHA256 hash of the blob content (content-addressable)
|
|
||||||
CreatedAt time.Time
|
|
||||||
Size int64
|
|
||||||
ChunkCount int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Snapshot represents a backup snapshot
|
|
||||||
type Snapshot struct {
|
|
||||||
ID string // ISO8601 timestamp
|
|
||||||
Hostname string
|
|
||||||
Version string
|
|
||||||
CreatedAt time.Time
|
|
||||||
FileCount int64
|
|
||||||
ChunkCount int64
|
|
||||||
BlobCount int64
|
|
||||||
TotalSize int64
|
|
||||||
MetadataSize int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// SnapshotMetadata contains the full metadata for a snapshot
|
|
||||||
type SnapshotMetadata struct {
|
|
||||||
Snapshot *Snapshot
|
|
||||||
Files map[string]*FileInfo
|
|
||||||
Chunks map[string]*ChunkInfo
|
|
||||||
Blobs map[string]*BlobInfo
|
|
||||||
FileChunks map[string][]*ChunkRef // path -> chunks
|
|
||||||
BlobChunks map[string][]*ChunkRef // blob hash -> chunks
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chunk represents a data chunk for processing
|
|
||||||
type Chunk struct {
|
|
||||||
Data []byte
|
|
||||||
Hash string
|
|
||||||
Offset int64
|
|
||||||
Length int64
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
package models_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"sneak.berlin/go/vaultik/internal/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestModelsCompilation ensures all model types can be instantiated
|
|
||||||
func TestModelsCompilation(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
// This test primarily serves as a compilation test
|
|
||||||
// to ensure all types are properly defined
|
|
||||||
|
|
||||||
// Test FileInfo
|
|
||||||
fi := &models.FileInfo{
|
|
||||||
Path: "/test/file.txt",
|
|
||||||
MTime: time.Now(),
|
|
||||||
Size: 1024,
|
|
||||||
}
|
|
||||||
if fi.Path != "/test/file.txt" {
|
|
||||||
t.Errorf("FileInfo.Path not set correctly")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test ChunkInfo
|
|
||||||
ci := &models.ChunkInfo{
|
|
||||||
Hash: "abc123",
|
|
||||||
Size: 512,
|
|
||||||
Offset: 0,
|
|
||||||
}
|
|
||||||
if ci.Hash != "abc123" {
|
|
||||||
t.Errorf("ChunkInfo.Hash not set correctly")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test BlobInfo
|
|
||||||
bi := &models.BlobInfo{
|
|
||||||
Hash: "blob123",
|
|
||||||
CreatedAt: time.Now(),
|
|
||||||
Size: 1024,
|
|
||||||
ChunkCount: 2,
|
|
||||||
}
|
|
||||||
if bi.Hash != "blob123" {
|
|
||||||
t.Errorf("BlobInfo.Hash not set correctly")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test Snapshot
|
|
||||||
s := &models.Snapshot{
|
|
||||||
ID: "2024-01-01T00:00:00Z",
|
|
||||||
Hostname: "test-host",
|
|
||||||
Version: "1.0.0",
|
|
||||||
CreatedAt: time.Now(),
|
|
||||||
}
|
|
||||||
if s.ID != "2024-01-01T00:00:00Z" {
|
|
||||||
t.Errorf("Snapshot.ID not set correctly")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# script/install-go: install the Go toolchain pinned by go.mod into the
|
|
||||||
# repo-local tool directory, verified against a committed sha256. Our
|
|
||||||
# own extension to scripts-to-rule-them-all. Idempotent: exits at once
|
|
||||||
# when the pinned toolchain is already installed.
|
|
||||||
#
|
|
||||||
# Only .gitea/workflows/release.yml calls this. goreleaser is not a
|
|
||||||
# compiler: it shells out to `go` for the `before:` hook and for every
|
|
||||||
# one of the four cross-compiles, so the release runner needs a Go
|
|
||||||
# toolchain on PATH. check.yml never does -- it builds inside the
|
|
||||||
# digest-pinned Dockerfile images -- so this is the release path's only
|
|
||||||
# host Go, and per REPO_POLICIES.md it must be pinned by hash.
|
|
||||||
# actions/setup-go exposes no checksum input, so Go is installed the way
|
|
||||||
# script/install-goreleaser installs goreleaser: download the exact
|
|
||||||
# archive from go.dev and refuse it unless its sha256 matches the value
|
|
||||||
# committed below.
|
|
||||||
#
|
|
||||||
# The version is go.mod's `go` directive, the single source of truth for
|
|
||||||
# the toolchain. GO_VERSION below MUST equal it, and this script fails
|
|
||||||
# when they disagree -- so bumping Go is one reviewed change touching
|
|
||||||
# go.mod, the checksum here, and the Dockerfile golang digest together.
|
|
||||||
#
|
|
||||||
# Linux only, because that is what the release runner is. A darwin dev
|
|
||||||
# building a snapshot uses their own Go; supporting an OS means adding
|
|
||||||
# its checksums.
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
|
||||||
|
|
||||||
# Go 1.26.1. Checksums are the sha256 values go.dev publishes for each
|
|
||||||
# archive at https://go.dev/dl/ (also in its ?mode=json manifest).
|
|
||||||
GO_VERSION="1.26.1"
|
|
||||||
SHA256_LINUX_AMD64="031f088e5d955bab8657ede27ad4e3bc5b7c1ba281f05f245bcc304f327c987a"
|
|
||||||
SHA256_LINUX_ARM64="a290581cfe4fe28ddd737dde3095f3dbeb7f2e4065cab4eae44dfc53b760c2f7"
|
|
||||||
|
|
||||||
GOROOT_DIR="$ROOT/.tool/go"
|
|
||||||
GOCMD="$GOROOT_DIR/bin/go"
|
|
||||||
|
|
||||||
# The `go` directive in go.mod, e.g. "1.26.1" from `go 1.26.1`.
|
|
||||||
gomod_go_version() {
|
|
||||||
sed -n 's/^go \([0-9][0-9.]*\).*/\1/p' "$ROOT/go.mod" | head -n 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Print the version of the go at $1 as "1.26.1", or nothing if it is not
|
|
||||||
# usable. `go version` prints "go version go1.26.1 linux/amd64".
|
|
||||||
go_version() {
|
|
||||||
[ -x "$1" ] || return 0
|
|
||||||
"$1" version 2>/dev/null |
|
|
||||||
sed -n 's/^go version go\([0-9][0-9.]*\) .*/\1/p' |
|
|
||||||
head -n 1
|
|
||||||
}
|
|
||||||
|
|
||||||
verify_sha256() {
|
|
||||||
file="$1"
|
|
||||||
want="$2"
|
|
||||||
if command -v sha256sum >/dev/null 2>&1; then
|
|
||||||
got="$(sha256sum "$file" | cut -d' ' -f1)"
|
|
||||||
elif command -v shasum >/dev/null 2>&1; then
|
|
||||||
got="$(shasum -a 256 "$file" | cut -d' ' -f1)"
|
|
||||||
else
|
|
||||||
echo "install-go: no sha256sum or shasum available" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
if [ "$got" != "$want" ]; then
|
|
||||||
echo "install-go: checksum mismatch for $file" >&2
|
|
||||||
echo " expected: $want" >&2
|
|
||||||
echo " actual: $got" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# On a Gitea/GitHub Actions runner, put the toolchain on PATH for the
|
|
||||||
# steps that follow by appending to the file named by $GITHUB_PATH. A
|
|
||||||
# no-op off CI, where the caller manages its own PATH.
|
|
||||||
export_ci_path() {
|
|
||||||
[ -n "${GITHUB_PATH:-}" ] || return 0
|
|
||||||
echo "$GOROOT_DIR/bin" >>"$GITHUB_PATH"
|
|
||||||
}
|
|
||||||
|
|
||||||
main() {
|
|
||||||
cd "$ROOT"
|
|
||||||
|
|
||||||
want="$(gomod_go_version)"
|
|
||||||
if [ "$want" != "$GO_VERSION" ]; then
|
|
||||||
echo "install-go: go.mod says go $want but this script pins" \
|
|
||||||
"$GO_VERSION." >&2
|
|
||||||
echo " Update GO_VERSION and the checksums in this script to" \
|
|
||||||
"match go.mod." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Already installed from a previous run? Then just fix PATH and stop.
|
|
||||||
if [ "$(go_version "$GOCMD")" = "$GO_VERSION" ]; then
|
|
||||||
echo "go $GO_VERSION already installed in .tool/go"
|
|
||||||
export_ci_path
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
os="$(uname -s)"
|
|
||||||
arch="$(uname -m)"
|
|
||||||
case "$os" in
|
|
||||||
Linux) os="linux" ;;
|
|
||||||
*)
|
|
||||||
echo "install-go: unsupported OS $os (release runner is Linux)" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
case "$arch" in
|
|
||||||
x86_64 | amd64)
|
|
||||||
arch="amd64"
|
|
||||||
sum="$SHA256_LINUX_AMD64"
|
|
||||||
;;
|
|
||||||
arm64 | aarch64)
|
|
||||||
arch="arm64"
|
|
||||||
sum="$SHA256_LINUX_ARM64"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "install-go: no pinned checksum for architecture $arch" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
archive="go${GO_VERSION}.${os}-${arch}.tar.gz"
|
|
||||||
url="https://go.dev/dl/${archive}"
|
|
||||||
|
|
||||||
if ! command -v curl >/dev/null 2>&1; then
|
|
||||||
echo "install-go: curl is required" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
dl="$(mktemp -d)"
|
|
||||||
mkdir -p "$ROOT/.tool"
|
|
||||||
stage="$(mktemp -d "$ROOT/.tool/.go-install.XXXXXX")"
|
|
||||||
# shellcheck disable=SC2064 # expand the paths now, not at trap time
|
|
||||||
trap "rm -rf '$dl' '$stage'" EXIT INT TERM
|
|
||||||
|
|
||||||
echo "installing go $GO_VERSION for ${os}-${arch}"
|
|
||||||
curl -fsSL --retry 3 -o "$dl/$archive" "$url"
|
|
||||||
verify_sha256 "$dl/$archive" "$sum"
|
|
||||||
|
|
||||||
# The archive unpacks to a top-level `go/` directory. Extract it into
|
|
||||||
# a staging directory on the same filesystem as the destination, then
|
|
||||||
# rename it into place so a concurrent run never observes a
|
|
||||||
# half-written toolchain.
|
|
||||||
tar -xzf "$dl/$archive" -C "$stage"
|
|
||||||
rm -rf "$GOROOT_DIR"
|
|
||||||
mv "$stage/go" "$GOROOT_DIR"
|
|
||||||
|
|
||||||
installed="$(go_version "$GOCMD")"
|
|
||||||
if [ "$installed" != "$GO_VERSION" ]; then
|
|
||||||
echo "install-go: installed toolchain reports '$installed'," \
|
|
||||||
"expected '$GO_VERSION'" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "go $GO_VERSION installed to .tool/go"
|
|
||||||
export_ci_path
|
|
||||||
}
|
|
||||||
|
|
||||||
main "$@"
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Vaultik test configuration
|
|
||||||
hostname: test-host
|
|
||||||
index_path: /tmp/vaultik-test/index.db
|
|
||||||
source_dirs:
|
|
||||||
- /tmp/vaultik-test/source
|
|
||||||
|
|
||||||
# S3 configuration
|
|
||||||
s3:
|
|
||||||
endpoint: http://localhost:19000 # gofakes3 test endpoint
|
|
||||||
bucket: test-bucket
|
|
||||||
prefix: test-
|
|
||||||
access_key_id: test-key
|
|
||||||
secret_access_key: test-secret
|
|
||||||
region: us-east-1
|
|
||||||
|
|
||||||
# Chunking configuration
|
|
||||||
chunk_size: 65536 # 64KB average chunk size
|
|
||||||
min_chunk_size: 32768 # 32KB minimum
|
|
||||||
max_chunk_size: 131072 # 128KB maximum
|
|
||||||
blob_size: 1048576 # 1MB blobs for testing
|
|
||||||
|
|
||||||
# Compression
|
|
||||||
compression_level: 3
|
|
||||||
|
|
||||||
# Encryption
|
|
||||||
# age_recipients:
|
|
||||||
# - age1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs3mw88h
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
age_recipients:
|
|
||||||
- age1278m9q7dp3chsh2dcy82qk27v047zywyvtxwnj4cvt0z65jw6a7q5dqhfj # sneak's long term age key
|
|
||||||
- age1ezrjmfpwsc95svdg0y54mums3zevgzu0x0ecq2f7tp8a05gl0sjq9q9wjg # insecure integration test key
|
|
||||||
source_dirs:
|
|
||||||
- /tmp/vaultik-test-source
|
|
||||||
exclude:
|
|
||||||
- '*.log'
|
|
||||||
- '*.tmp'
|
|
||||||
- '.git'
|
|
||||||
- 'node_modules'
|
|
||||||
s3:
|
|
||||||
endpoint: http://ber1app1.local:3900/
|
|
||||||
bucket: vaultik-integration-test
|
|
||||||
prefix: test-host/
|
|
||||||
access_key_id: GKbc8e6d35fdf50847f155aca5
|
|
||||||
secret_access_key: 217046bee47c050301e3cc13e3cba1a8a943cf5f37f8c7979c349c5254441d18
|
|
||||||
region: us-east-1
|
|
||||||
use_ssl: false
|
|
||||||
part_size: 5242880 # 5MB
|
|
||||||
index_path: /tmp/vaultik-integration-test.sqlite
|
|
||||||
chunk_size: 10MB
|
|
||||||
blob_size_limit: 10GB
|
|
||||||
compression_level: 3
|
|
||||||
hostname: test-host
|
|
||||||
Reference in New Issue
Block a user