diff --git a/doc/packages/index.md b/doc/packages/index.md index 45ef55f75a80..fd7ca33cf037 100644 --- a/doc/packages/index.md +++ b/doc/packages/index.md @@ -25,6 +25,7 @@ etc-files.section.md nginx.section.md nrfutil.section.md opengl.section.md +packer.section.md shell-helpers.section.md python-tree-sitter.section.md treefmt.section.md diff --git a/doc/packages/packer.section.md b/doc/packages/packer.section.md new file mode 100644 index 000000000000..7dbe4dfe3775 --- /dev/null +++ b/doc/packages/packer.section.md @@ -0,0 +1,70 @@ +# Packer {#sec-packer} + +[Packer](https://www.packer.io) is a tool for creating identical machine images +for multiple platforms from a single source configuration. + +## Using Packer with plugins {#sec-packer-with-plugins} + +Packer's functionality is extended through +[plugins](https://developer.hashicorp.com/packer/docs/plugins). Rather than +letting Packer download plugins at runtime, you can build a Packer wrapper that +bundles the plugins you need with `packer.withPlugins`. + +`packer.withPlugins` takes a function that receives the set of available plugins +and returns the list of plugins to include: + +```nix +packer.withPlugins (ps: [ ps.docker ]) +``` + +This produces a `packer` executable wrapped with the `PACKER_PLUGIN_PATH` +environment variable set, so the selected plugins are available without a +separate `packer plugins install` step. + +For example, to get a development shell with Packer and the Docker plugin: + +```nix +{ + pkgs ? import { }, +}: + +pkgs.mkShell { + packages = [ + (pkgs.packer.withPlugins (ps: [ ps.docker ])) + ]; +} +``` + +Multiple plugins can be selected at once: + +```nix +packer.withPlugins (ps: [ + ps.docker + ps.qemu +]) +``` + +## Listing available plugins {#sec-packer-list-plugins} + +The packaged plugins are exposed as the `packer.plugins` attribute set. To list +every plugin available in your version of Nixpkgs, query its attribute names: + +```ShellSession +$ nix eval nixpkgs#packer.plugins --apply builtins.attrNames +[ "docker" "qemu" ] +``` + +Without flakes: + +```ShellSession +$ nix-env -f '' -qaP -A packer.plugins +packer.plugins.docker packer-plugin-docker-1.1.2 +packer.plugins.qemu packer-plugin-qemu-1.1.4 +``` + +The attribute name (for example `docker` or `qemu`) is what you pass to +`packer.withPlugins`. + +Notes: + +- `mkPackerPlugin` currently only supports `fetchFromGitHub` as the fetcher. diff --git a/doc/redirects.json b/doc/redirects.json index 21e7d9a68d31..f811c5a67d13 100644 --- a/doc/redirects.json +++ b/doc/redirects.json @@ -620,6 +620,15 @@ "chap-overrides": [ "index.html#chap-overrides" ], + "sec-packer": [ + "index.html#sec-packer" + ], + "sec-packer-list-plugins": [ + "index.html#sec-packer-list-plugins" + ], + "sec-packer-with-plugins": [ + "index.html#sec-packer-with-plugins" + ], "sec-pkg-override": [ "index.html#sec-pkg-override" ], diff --git a/pkgs/by-name/pa/packer/extra/mk-packer-plugin.nix b/pkgs/by-name/pa/packer/extra/mk-packer-plugin.nix new file mode 100644 index 000000000000..b5a8cf71711e --- /dev/null +++ b/pkgs/by-name/pa/packer/extra/mk-packer-plugin.nix @@ -0,0 +1,100 @@ +let + platformSuffix = { + x86_64-linux = "linux_amd64"; + x86_64-darwin = "darwin_amd64"; + aarch64-linux = "linux_arm64"; + aarch64-darwin = "darwin_arm64"; + }; +in + +{ + stdenv, + lib, + buildGoModule, + versionCheckHook, + nix-update-script, +}: + +lib.extendMkDerivation { + constructDrv = buildGoModule; + + excludeDrvArgNames = [ + "apiVersion" + ]; + + extendDrvArgs = + finalAttrs: + { + pname, + version, + src, + ldflags ? [ ], + nativeInstallCheckInputs ? [ ], + postFixup ? "", + subPackages ? [ "." ], + apiVersion ? "x5.0", + versionCheckProgramArg ? "describe", + doInstallCheck ? true, + ... + }@prevAttrs: + # Packer expects plugins to be hosted on GitHub and follow a specific release structure + # (see: https://developer.hashicorp.com/packer/docs/plugins/creation#creating-a-github-release). + # This introduces two requirements: + # + # 1. Directory Structure: Packer expects the plugin binary to reside in: + # $PACKER_PLUGIN_PATH/github.com/$OWNER/$TYPE/ + # where $TYPE is the repo name without the "packer-plugin-" prefix. + # + # 2. Configuration Source: When declaring the plugin in a Packer template, the `source` + # attribute must match this GitHub address format (e.g.: "github.com/$OWNER/$TYPE") + let + _ = lib.assertMsg ( + src ? repo && src ? owner && src ? githubBase + ) "mk-packer-plugin: fetchFromGitHub is currently the only supported fetcher"; + suffix = + platformSuffix."${stdenv.hostPlatform.system}" + or (throw "Unsupported system: ${stdenv.hostPlatform.system}"); + binName = "${finalAttrs.src.repo}_v${finalAttrs.version}_${apiVersion}_${suffix}"; + in + { + inherit + subPackages + doInstallCheck + versionCheckProgramArg + ; + + __structuredAttrs = true; + strictDeps = true; + + ldflags = + ldflags + ++ ( + let + versionFlag = "${finalAttrs.src.githubBase}/${finalAttrs.src.owner}/${finalAttrs.src.repo}/version"; + in + [ + "-s" + "-w" + "-X ${versionFlag}.Version=${finalAttrs.version}" + "-X ${versionFlag}.VersionPrerelease=" + ] + ); + + versionCheckProgram = prevAttrs.versionCheckProgram or "${placeholder "out"}/bin/${binName}"; + + nativeInstallCheckInputs = nativeInstallCheckInputs ++ [ versionCheckHook ]; + + # Generate checksums AFTER fixup phase when binary is finalized + postFixup = postFixup + '' + mv "$out/bin/${finalAttrs.src.repo}" "$out/bin/${binName}" + sha256sum "$out/bin/${binName}" | cut -d' ' -f1 > "$out/bin/${binName}_SHA256SUM" + ''; + + meta.mainProgram = binName; + + passthru = { + pluginPath = "${finalAttrs.src.githubBase}/${finalAttrs.src.owner}/${lib.removePrefix "packer-plugin-" finalAttrs.src.repo}/${binName}"; + updateScript = prevAttrs.passthru.updateScript or (nix-update-script { }); + }; + }; +} diff --git a/pkgs/by-name/pa/packer/package.nix b/pkgs/by-name/pa/packer/package.nix index 6d59320d504f..9989270b49dc 100644 --- a/pkgs/by-name/pa/packer/package.nix +++ b/pkgs/by-name/pa/packer/package.nix @@ -3,16 +3,18 @@ buildGoModule, fetchFromGitHub, installShellFiles, + callPackage, + runCommand, }: -buildGoModule rec { +buildGoModule (finalAttrs: { pname = "packer"; version = "1.15.4"; src = fetchFromGitHub { owner = "hashicorp"; repo = "packer"; - rev = "v${version}"; + tag = "v${finalAttrs.version}"; hash = "sha256-mhHES+/FCvVBBQm1qDQeH6WY2c9hIV7N3iFBCqJqJLw="; }; @@ -31,6 +33,46 @@ buildGoModule rec { installShellCompletion --zsh contrib/zsh-completion/_packer ''; + passthru = + let + pluginScope = callPackage ./plugins.nix { }; + in + { + plugins = lib.filterAttrs (_: lib.isDerivation) pluginScope; + + withPlugins = + f: + (callPackage ./with-plugins.nix { + packer = finalAttrs.finalPackage; + packerPlugins = pluginScope; + }) + { selector = f; }; + + tests.withPlugins = + let + packer-with-docker = finalAttrs.passthru.withPlugins (ps: [ ps.docker ]); + in + runCommand "packer-test-with-plugins" + { + nativeBuildInputs = [ packer-with-docker ]; + } + '' + packer plugins installed > output.txt + cat output.txt + + expected_path="${finalAttrs.passthru.plugins.docker.pluginPath}" + if ! grep -q "$expected_path" output.txt; then + echo "ERROR: Expected plugin path not found in 'packer plugins installed' output" + echo "Expected: $expected_path" + echo "Got:" + cat output.txt + exit 1 + fi + + touch $out + ''; + }; + meta = { description = "Tool for creating identical machine images for multiple platforms from a single source configuration"; homepage = "https://www.packer.io"; @@ -40,7 +82,8 @@ buildGoModule rec { ma27 techknowlogick qjoly + jlesquembre ]; - changelog = "https://github.com/hashicorp/packer/blob/v${version}/CHANGELOG.md"; + changelog = "https://github.com/hashicorp/packer/blob/v${finalAttrs.version}/CHANGELOG.md"; }; -} +}) diff --git a/pkgs/by-name/pa/packer/plugins.nix b/pkgs/by-name/pa/packer/plugins.nix new file mode 100644 index 000000000000..8262af1d7641 --- /dev/null +++ b/pkgs/by-name/pa/packer/plugins.nix @@ -0,0 +1,19 @@ +{ + lib, + newScope, +}: +lib.makeScope newScope ( + self: + let + packages = lib.packagesFromDirectoryRecursive { + inherit (self) callPackage; + directory = ./plugins; + }; + in + { + mkPackerPlugin = self.callPackage ./extra/mk-packer-plugin.nix { }; + } + // lib.mapAttrs' ( + name: pkg: lib.nameValuePair (lib.removePrefix "packer-plugin-" name) pkg + ) packages +) diff --git a/pkgs/by-name/pa/packer/plugins/packer-plugin-docker/package.nix b/pkgs/by-name/pa/packer/plugins/packer-plugin-docker/package.nix new file mode 100644 index 000000000000..d1ed5c201dfc --- /dev/null +++ b/pkgs/by-name/pa/packer/plugins/packer-plugin-docker/package.nix @@ -0,0 +1,27 @@ +{ + lib, + mkPackerPlugin, + fetchFromGitHub, +}: + +mkPackerPlugin (finalAttrs: { + + pname = "packer-plugin-docker"; + version = "1.1.2"; + + src = fetchFromGitHub { + owner = "hashicorp"; + repo = "packer-plugin-docker"; + tag = "v${finalAttrs.version}"; + hash = "sha256-h89FGwUQjTk81CYUe1xmKxSHr1t3wyBg9lHUTqmVym8="; + }; + + vendorHash = "sha256-mvyafYSLi/q7lWorfKc4Gc4oM7yti3v/bLcVnNkH7ZY="; + + meta = { + description = "Packer plugin for Docker"; + homepage = "https://github.com/hashicorp/packer-plugin-docker"; + license = lib.licenses.mpl20; + maintainers = with lib.maintainers; [ jlesquembre ]; + }; +}) diff --git a/pkgs/by-name/pa/packer/plugins/packer-plugin-qemu/package.nix b/pkgs/by-name/pa/packer/plugins/packer-plugin-qemu/package.nix new file mode 100644 index 000000000000..40eba546df60 --- /dev/null +++ b/pkgs/by-name/pa/packer/plugins/packer-plugin-qemu/package.nix @@ -0,0 +1,27 @@ +{ + lib, + mkPackerPlugin, + fetchFromGitHub, +}: + +mkPackerPlugin (finalAttrs: { + + pname = "packer-plugin-qemu"; + version = "1.1.4"; + + src = fetchFromGitHub { + owner = "hashicorp"; + repo = "packer-plugin-qemu"; + tag = "v${finalAttrs.version}"; + hash = "sha256-Ose7ueo9V2zJ5K5yvew9ErTD9lR+rkp1UB/yDi+U+fY="; + }; + + vendorHash = "sha256-pgfI/ntG6Fesimw3jk70GP+lBUEUMfq6wbqXx8xCTf0="; + + meta = { + description = "Packer plugin for QEMU"; + homepage = "https://github.com/hashicorp/packer-plugin-qemu"; + license = lib.licenses.mpl20; + maintainers = with lib.maintainers; [ jlesquembre ]; + }; +}) diff --git a/pkgs/by-name/pa/packer/with-plugins.nix b/pkgs/by-name/pa/packer/with-plugins.nix new file mode 100644 index 000000000000..266b0be47627 --- /dev/null +++ b/pkgs/by-name/pa/packer/with-plugins.nix @@ -0,0 +1,54 @@ +{ + lib, + packer, + packerPlugins, + makeBinaryWrapper, + linkFarm, + stdenvNoCC, +}: +lib.extendMkDerivation { + constructDrv = stdenvNoCC.mkDerivation; + + excludeDrvArgNames = [ + "derivationArgs" + "selector" + ]; + + extendDrvArgs = + finalAttrs: + { + selector ? (_: [ ]), + name ? "packer-with-plugins", + nativeBuildInputs ? [ makeBinaryWrapper ], + ... + }: + let + plugins = selector packerPlugins; + pluginFarm = linkFarm "packer-plugins" ( + builtins.concatMap (p: [ + { + name = p.pluginPath; + path = "${p}/bin/${p.meta.mainProgram}"; + } + { + name = "${p.pluginPath}_SHA256SUM"; + path = "${p}/bin/${p.meta.mainProgram}_SHA256SUM"; + } + ]) plugins + ); + in + { + inherit name nativeBuildInputs; + __structuredAttrs = true; + strictDeps = true; + + meta.mainProgram = "packer"; + + dontUnpack = true; + + buildCommand = '' + makeWrapper "${packer}/bin/packer" "$out/bin/packer" \ + --set PACKER_PLUGIN_PATH "${pluginFarm}" + ''; + }; +}