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

Add module builder #48

Open
wants to merge 7 commits 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
51 changes: 51 additions & 0 deletions .github/workflows/update-module-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Update module docs
on:
workflow_dispatch: # allows manual triggering

jobs:
update-docs:
if: github.repository_owner == 'astro'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v23
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}

- name: Set environment variables
shell: bash
run: |
echo "GIT_AUTHOR_NAME=github-actions[bot]" >> $GITHUB_ENV
echo "GIT_AUTHOR_EMAIL=<github-actions[bot]@users.noreply.github.com>" >> $GITHUB_ENV
echo "GIT_COMMITTER_NAME=github-actions[bot]" >> $GITHUB_ENV
echo "GIT_COMMITTER_EMAIL=<github-actions[bot]@users.noreply.github.com>" >> $GITHUB_ENV

- name: Update docs
shell: bash
run: |
nix run .#update-module-docs

- name: Check for changes
id: git-check
shell: bash
run: |
git diff --quiet || echo "changes_detected=true" >> $GITHUB_OUTPUT

- name: Commit changes
if: steps.git-check.outputs.changes_detected
shell: bash
run: |
git commit --message "Update module docs" || true

- name: Push changes
if: steps.git-check.outputs.changes_detected
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ github.ref }}
112 changes: 86 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,13 @@ in

## Usage with Nix Flakes

```nix
### Using build module (recommended)

nix-openwrt-imagebuilder exposes a builder module that leverages [nixpkgs' module system](https://nixos.org/manual/nixpkgs/stable/#module-system) to provide easy to use, self-documenting interface with strict option definitions, type-checks.

See documentation for all available options in [docs](docs/).

```nix
{
inputs = {
openwrt-imagebuilder.url = "github:astro/nix-openwrt-imagebuilder";
Expand All @@ -67,36 +73,90 @@ in
packages.x86_64-linux.my-router =
let
pkgs = nixpkgs.legacyPackages.x86_64-linux;

profiles = openwrt-imagebuilder.lib.profiles { inherit pkgs; };

config = profiles.identifyProfile "avm_fritz7412" // {
# add package to include in the image, ie. packages that you don't
# want to install manually later
packages = [ "tcpdump" ];

disabledServices = [ "dnsmasq" ];

# include files in the images.
# to set UCI configuration, create a uci-defauts scripts as per
# official OpenWRT ImageBuilder recommendation.
files = pkgs.runCommand "image-files" {} ''
mkdir -p $out/etc/uci-defaults
cat > $out/etc/uci-defaults/99-custom <<EOF
uci -q batch << EOI
set system.@system[0].hostname='testap'
commit
EOI
EOF
'';
};

in
openwrt-imagebuilder.lib.build config;
openwrt-imagebuilder.lib.build-module {
inherit pkgs;
modules = [
{
release = "23.05.5";
hardware = {
profile = "tplink_archer-c7-v2";
};
packages = {
include = [
"tcpdump"
"vxlan"
"kmod-vxlan"
];
};
files = [
{
source = pkgs.writeText "uci-defaults-99-custom" ''
uci -q batch << EOI
set system.@system[0].hostname='testap'
commit
EOI
'';
target = "/etc/uci-defaults/99-custom";
}
];
}
];
};
};
}

```

### Using builder function (deprecated)

This is a deprecated approach and eventually will be removed.

<details>

<summary>Code</summary>

```nix
{
inputs = {
openwrt-imagebuilder.url = "github:astro/nix-openwrt-imagebuilder";
};
outputs = { self, nixpkgs, openwrt-imagebuilder }: {
packages.x86_64-linux.my-router =
let
pkgs = nixpkgs.legacyPackages.x86_64-linux;

profiles = openwrt-imagebuilder.lib.profiles { inherit pkgs; };

config = profiles.identifyProfile "avm_fritz7412" // {
# add package to include in the image, ie. packages that you don't
# want to install manually later
packages = [ "tcpdump" ];

disabledServices = [ "dnsmasq" ];

# include files in the images.
# to set UCI configuration, create a uci-defauts scripts as per
# official OpenWRT ImageBuilder recommendation.
files = pkgs.runCommand "image-files" {} ''
mkdir -p $out/etc/uci-defaults
cat > $out/etc/uci-defaults/99-custom <<EOF
uci -q batch << EOI
set system.@system[0].hostname='testap'
commit
EOI
EOF
'';
};

in
openwrt-imagebuilder.lib.build config;
};
}
```

</details>

## Refreshing hashes

**downloads.openwrt.org** appears to be never at rest. That's why we
Expand Down
16 changes: 16 additions & 0 deletions builder/build-module.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{ lib }:
let
# Inspired by https://github.com/viperML/wrapper-manager/blob/c936f9203217e654a6074d206505c16432edbc70/default.nix
eval = { pkgs, modules }: lib.evalModules {
modules = [
{ _module.args = { inherit pkgs; }; }
./module-options.nix
./module-config.nix
] ++ modules;
};
in
{
# Using functor here to provide a debug output with `eval` if needed, but by default just build the eval the result
inherit eval;
__functor = self: args: (self.eval args).config.build.out;
}
178 changes: 178 additions & 0 deletions builder/module-config.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
{ config, pkgs, lib, ... }:
let
inherit (import ../files.nix {
inherit pkgs;
inherit (config) release;
inherit (config.hardware) target;
variant = config.hardware.subtarget;
pedorich-n marked this conversation as resolved.
Show resolved Hide resolved
packagesArch = config.hardware.arch;
feedsSha256 = config.feedsHash;
kmodsSha256 = config.kmodsHash;
sha256 = config.sumsFileHash;
}) variantFiles profiles expandDeps corePackages packagesByFeed allPackages;


customPackagesFormatted = config.packages.include ++ (builtins.map (pkg: "-${pkg}") config.packages.exclude);

requiredPackages =
profiles.default_packages or (builtins.attrNames packagesByFeed.base ++ builtins.attrNames corePackages) ++
profiles.profiles.${config.hardware.profile}.device_packages or [ ] ++
customPackagesFormatted;

allRequiredPackageFiles = expandDeps allPackages requiredPackages;

mkPackageSymlink = pname:
let
package = allPackages.${pname};
in
lib.optional (package.type == "real") ''
[ -e "packages/${package.filename}" ] || ln -s ${package.file} "packages/${package.filename}"
'';

symlinkPackages = pkgs.writeScript "symlink-openwrt-packages" (
lib.concatLines (
lib.foldl' (acc: pname: acc ++ (mkPackageSymlink pname)) [ ] allRequiredPackageFiles
)
);

mkCopyStatement = file: ''
(
target_rel=${lib.escapeShellArg file.target}
target="files/''${target_rel#/}"
${lib.optionalString (lib.pathIsRegularFile file.source) ''
mkdir -p "$(dirname "''${target}")"
''}
cp -r --no-preserve=all "${file.source}" "''${target}"
)
'';

copyFiles = pkgs.writeScript "copy-openwrt-files" (
lib.concatLines (builtins.map mkCopyStatement config.files)
);

buildFlags = {
".DEFAULT_GOAL" = "image";
"PROFILE" = config.hardware.profile;
} // lib.optionalAttrs (customPackagesFormatted != [ ]) {
"PACKAGES" = lib.concatStringsSep " " customPackagesFormatted;
} // lib.optionalAttrs (config.disabledServices != [ ]) {
"DISABLED_SERVICES" = lib.concatStringsSep " " config.disabledServices;
} // lib.optionalAttrs (config.files != [ ]) {
"FILES" = "./files";
} // lib.optionalAttrs (config.image.rootFsPartSize != null) {
"ROOTFS_PARTSIZE" = config.image.rootFsPartSize;
} // lib.optionalAttrs (config.image.extraName != null) {
"EXTRA_IMAGE_NAME" = config.image.extraName;
} // lib.optionalAttrs (config.image.addLocalKey != null) {
"ADD_LOCAL_KEY" = lib.boolToString config.image.addLocalKey;
} // config.build.extraFlags;

buildFlagsArray = lib.concatStringsSep " " (lib.mapAttrsToList (name: value: ''${name}="${builtins.toString value}"'') buildFlags);

name = lib.concatStringsSep "-" (
[ "openwrt" config.release ] ++
lib.optional (config.image.extraName != null) config.image.extraName ++
[ config.hardware.target config.hardware.subtarget config.hardware.profile ]
);

src =
let
inherit (pkgs.stdenv.hostPlatform) uname;
imageBuilderPrefix = "openwrt-imagebuilder-${lib.optionalString (config.release != "snapshot") "${config.release}-"}";
baseFileName = "${imageBuilderPrefix}${config.hardware.target}-${config.hardware.subtarget}.${uname.system}-${uname.processor}";
possibleFileNames = builtins.map (extension: "${baseFileName}${extension}") [ ".tar.zst" ".tar.xz" ];
matches = builtins.filter (fileName: builtins.hasAttr fileName variantFiles) possibleFileNames;
in
if matches != [ ]
then builtins.getAttr (builtins.elemAt matches 0) variantFiles
else
builtins.throw ''
No valid image builder found!
Expected filenames: ${lib.concatStringsSep ", " possibleFileNames}
'';
in
{

config = {
build.out = pkgs.stdenv.mkDerivation ({
inherit name src;

nativeBuildInputs = with pkgs; [
bash
bzip2
dtc
file
getopt
git
ncurses
perl
python311
rsync
unzip
wget
which
zlib
zstd
] ++ lib.optional (lib.versionOlder config.release "21" && config.release != "snapshot") python2;

postPatch = ''
patchShebangs scripts staging_dir/host/bin
substituteInPlace rules.mk \
--replace "SHELL:=/usr/bin/env bash" "SHELL:=${pkgs.runtimeShell}" \
--replace "/usr/bin/env true" "${pkgs.coreutils}/bin/true" \
--replace "/usr/bin/env false" "${pkgs.coreutils}/bin/false"
'';

configurePhase = ''
runHook preConfigure

${symlinkPackages}
echo "src imagebuilder file:packages" > repositories.conf

${copyFiles}

# if the user provided key-build, key-build.pub and key-build.ucert in /run/openwrt use it
# NOTE: they need to be owned by group nixbld and have permission 440
# NOTE2: auto-allocate-uids must be disabled because of bug https://github.com/NixOS/nix/issues/9276
if [[ -d /run/openwrt ]]; then
for file in /run/openwrt/*; do
ln -s $file $(basename $file)
done
fi
'' + lib.optionalString (config.files != [ ]) ''
${copyFiles}
'' + lib.optionalString (lib.versionOlder config.release "19" && config.release != "snapshot") ''
# hack around broken check for gcc
touch staging_dir/host/.prereq-build
'' + ''

runHook postConfigure
'';

preBuild = ''
buildFlagsArray+=(${buildFlagsArray})
'';

installPhase = ''
runHook preInstall

mkdir -p $out/nix-support
pushd bin/targets/${config.hardware.target}/${config.hardware.subtarget}
for src in *; do
dst="$out/$src"
cp -ar "$src" "$dst"
if [ -f "$dst" ]; then
filename=$(basename "$dst")
echo "file ''${filename##*.} $dst" >> $out/nix-support/hydra-build-products
fi
done
popd

runHook postInstall
'';

dontFixup = true;

} // config.build.extraDerivationArgs);
};
}
Loading