Skip to content

Commit

Permalink
feat: Create a Nushell script for 'files' module
Browse files Browse the repository at this point in the history
  • Loading branch information
gmpinder committed Jan 19, 2025
1 parent 64da850 commit 395cda2
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 10 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/build-individual.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: build-individual
on:
push:
branches:
- main
paths-ignore: # don't rebuild if only documentation has changed
- "**.md"
pull_request:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/build-unified.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: build-unified
on:
push:
branches:
- main
paths-ignore: # don't rebuild if only documentation has changed
- "**.md"
pull_request:
Expand Down
38 changes: 28 additions & 10 deletions modules/files/files.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,33 @@ import "@typespec/json-schema";
using TypeSpec.JsonSchema;

@jsonSchema("/modules/files.json")
model FilesModule {
/** Copy files to your image at build time
* https://blue-build.org/reference/modules/files/
*/
type: "files";
union FilesModule {
FilesV1,
FilesV2,
}

model FilesV1 {
/** Copy files to your image at build time
* https://blue-build.org/reference/modules/files/
*/
type: "files@v1";

/** List of files / folders to copy. */
files: Array<Record<string>> | Array<{
source: string;
destination: string;
}>;
}

model FilesV2 {
/** Copy files to your image at build time
* https://blue-build.org/reference/modules/files/
*/
type: "files@v2" | "files@latest" | "files";

/** List of files / folders to copy. */
files: Array<Record<string>> | Array<{
source: string;
destination: string;
}>;
/** List of files / folders to copy. */
files: Array<{
source: string;
destination: string;
}>;
}
File renamed without changes.
File renamed without changes.
46 changes: 46 additions & 0 deletions modules/files/v2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# `files`

The `files` module can be used to copy directories from `files/` to
any location in your image at build-time, as long as the location exists at
build-time (e.g. you can't put files in `/home/<username>/`, because users
haven't been created yet prior to first boot).

:::note
In run-time, `/usr/etc/` is the directory for "system"
configuration templates on atomic Fedora distros, whereas `/etc/` is meant for
manual overrides and editing by the machine's admin *after* installation.

In build-time, as a custom-image maintainer, you want to copy files to `/etc/`,
as those are automatically moved to system directory `/usr/etc/` during atomic Fedora image deployment.
Check out this blog post for more details about this:
https://blue-build.org/blog/preferring-system-etc/
:::

:::caution
The `files` module **cannot write to directories that will later be symlinked
to point to other places (typically `/var/`) by `rpm-ostree`**.

This is because it doesn't make sense for a directory to be both a symlink and
a real directory that has had actual files directly copied to it, so the
`files` module copying files to one of those directories (thereby instantiating
it as a real directory) and `rpm-ostree`'s behavior regarding them will
necessarily conflict.

For reference, according to the [official Fedora
documentation](https://docs.fedoraproject.org/en-US/fedora-silverblue/technical-information/#filesystem-layout),
here is a list of the directories that `rpm-ostree` symlinks to other
locations:

- `/home/``/var/home/`
- `/opt/``/var/opt/`
- `/srv/``/var/srv/`
- `/root/``/var/roothome/`
- `/usr/local/``/var/usrlocal/`
- `/mnt/``/var/mnt/`
- `/tmp/``/sysroot/tmp/`

So don't use `files` to copy any files to any of the directories on the left,
because at runtime `rpm-ostree` will want to link them to the ones on the
right, which will cause a conflict as explained above.

:::
72 changes: 72 additions & 0 deletions modules/files/v2/files.nu
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env nu

def determine_file_dir []: nothing -> string {
let config_dir = $env.CONFIG_DIRECTORY

if $config_dir == '/tmp/config' {
$config_dir | path join 'files'
} else {
$config_dir
}
}

def main [config: string]: nothing -> nothing {
let config = $config | from json
let files: list = $config.files
let list_is_empty = $files | is-empty

if $list_is_empty {
return (error make {
msg: $"(ansi red_bold)At least one entry is required in property(ansi reset) `(ansi cyan)files(ansi reset)`:\n($config | to yaml)"
label: {
text: 'Checks for empty list'
span: (metadata $list_is_empty).span
}
})
}

let config_dir = determine_file_dir

for $file in $files {
let file = $file | merge { source: ($config_dir | path join $file.source) }
let source = $file.source
let destination = $file.destination
let source_exists = not ($source | path exists)
let is_dir = ($destination | path exists) and ($destination | path type) == 'file'

if $source_exists {
return (error make {
msg: $"(ansi red_bold)The path (ansi cyan)`($source)`(ansi reset) (ansi red_bold)does not exist(ansi reset):\n($config | to yaml)"
label: {
text: 'Checks for source'
span: (metadata $source_exists).span
}
})
}

if $is_dir {
return (error make {
msg: $"(ansi red_bold)The destination path (ansi cyan)`($destination)`(ansi reset) (ansi red_bold)should be a directory(ansi reset):\n($config | to yaml)"
label: {
text: 'Checks destination is directory'
span: (metadata $is_dir).span
}
})
}

print $'Copying (ansi cyan)($source)(ansi reset) to (ansi cyan)($destination)(ansi reset)'
mkdir $destination

if ($source | path type) == 'dir' {
cp -rfv ($source | path join * | into glob) $destination
} else {
cp -fv $source $destination
}

let git_keep = $destination | path join '.gitkeep'

if ($git_keep | path exists) {
rm -f $git_keep
}
}
}

0 comments on commit 395cda2

Please sign in to comment.