forked from efforting.tech/gitea.efforting.tech
Refine checkout definition hashing and architecture docs
This commit is contained in:
@@ -74,13 +74,15 @@ async function compose(compose_config, args) {
|
||||
})
|
||||
}
|
||||
|
||||
// Hash (owner, repo_url, revision) to get an unambiguous volume name.
|
||||
// Human-readable metadata is stored as Docker volume labels instead.
|
||||
function worktree_volume_name(owner, repo_url, revision) {
|
||||
// Hash the entire checkout definition to get an unambiguous volume name.
|
||||
// Any change to any field (repo, revision, key, submodules, etc.) produces
|
||||
// a different volume. Human-readable metadata is stored as Docker volume labels.
|
||||
function worktree_volume_name(owner, repo_def) {
|
||||
const sorted_keys = Object.keys(repo_def).sort()
|
||||
const canonical = JSON.stringify(Object.fromEntries(sorted_keys.map(k => [k, repo_def[k]])))
|
||||
const hash = createHash('sha256')
|
||||
.update(owner).update('\0')
|
||||
.update(repo_url).update('\0')
|
||||
.update(revision)
|
||||
.update(canonical)
|
||||
.digest('hex')
|
||||
.slice(0, 24)
|
||||
return `ci-worktree-${hash}`
|
||||
@@ -90,8 +92,9 @@ function worktree_volume_name(owner, repo_url, revision) {
|
||||
// Step 1: Prepare base worktree volume
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function prepare_worktree(owner, repo_url, revision, mutable, private_key_path) {
|
||||
const volume_name = worktree_volume_name(owner, repo_url, revision)
|
||||
async function prepare_worktree(owner, repo_def, private_key_path) {
|
||||
const { repo_url, revision, mutable } = repo_def
|
||||
const volume_name = worktree_volume_name(owner, repo_def)
|
||||
|
||||
// Check if the volume already exists
|
||||
let exists = false
|
||||
@@ -103,7 +106,7 @@ async function prepare_worktree(owner, repo_url, revision, mutable, private_key_
|
||||
}
|
||||
|
||||
if (exists && !mutable) {
|
||||
// Immutable revision — volume is permanent once created
|
||||
// Immutable — volume is permanent once created
|
||||
console.log(`Base worktree volume ${volume_name} already exists, reusing.`)
|
||||
return volume_name
|
||||
}
|
||||
@@ -189,7 +192,7 @@ async function main() {
|
||||
const repo = checkout.repositories[0]
|
||||
const private_key_path = path.join(secrets_base, 'git-ssh', user, 'private', repo.key)
|
||||
|
||||
const worktree_volume = await prepare_worktree(user, repo.repo_url, repo.revision, repo.mutable, private_key_path)
|
||||
const worktree_volume = await prepare_worktree(user, repo, private_key_path)
|
||||
console.log(`\nWorktree volume: ${worktree_volume}\n`)
|
||||
|
||||
const container_name = `ci-rsync-${worktree_volume.slice('ci-worktree-'.length)}`
|
||||
|
||||
@@ -16,13 +16,13 @@ Images and tasks are declared separately. A task references an image by name. Mu
|
||||
|
||||
### Worktree layers (overlayfs)
|
||||
|
||||
Each run mounts a single worktree into the container, but this is composed from two separately managed Docker volumes:
|
||||
A run may check out one or more repositories. Each repository produces two separately managed Docker volumes:
|
||||
|
||||
**Base worktree** — a named Docker volume containing a plain `git checkout` of a specific `(owner, repo, revision)`. Read-only from the task container's perspective. Whether it is reused or re-checked-out on each run depends on whether the revision is mutable (see below).
|
||||
**Base worktree** — a named Docker volume containing a plain `git checkout` of a specific repository at a specific revision. The volume name is derived from a hash of the entire checkout definition (owner, repo URL, revision, key, submodules flag, etc.) so any change to any field produces a distinct volume. Keys are sorted before hashing so field order in the config file does not affect the result. Read-only from the task container's perspective. Whether it is reused or re-checked-out on each run depends on whether the revision is mutable (see below).
|
||||
|
||||
**Worktree mutations** — a named Docker volume holding the overlayfs upper layer for a specific run. Unique per run. Contains only what the container wrote or modified — the base worktree is never touched.
|
||||
|
||||
From inside the container these appear as a single mount point (the overlayfs merged view). From outside, the CI system names and manages them separately.
|
||||
Each repository is mounted at a distinct path inside the container. From the outside, the CI system names and manages the volumes per repository independently.
|
||||
|
||||
Using named Docker volumes means the checkout is a straightforward `git clone` or `git checkout` into a volume — no need for the `--git-dir` / `--work-tree` / `GIT_INDEX_FILE` technique. That approach remains useful for direct host-based deployments but is out of scope here.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user