Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Create a Nushell script for 'files' module #382

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
}
}
Loading