diff --git a/docs/ioc/user-guides/deprecations/index.rst b/docs/ioc/user-guides/deprecations/index.rst new file mode 100644 index 00000000..49b90ac4 --- /dev/null +++ b/docs/ioc/user-guides/deprecations/index.rst @@ -0,0 +1,10 @@ +Deprecations +============ + +Guides for how to migrate your code +due to a deprecation in the EPNix project + +.. toctree:: + :titlesonly: + + ./migrating-from-modules-development.rst diff --git a/docs/ioc/user-guides/deprecations/migrating-from-modules-development.rst b/docs/ioc/user-guides/deprecations/migrating-from-modules-development.rst new file mode 100644 index 00000000..9bafe973 --- /dev/null +++ b/docs/ioc/user-guides/deprecations/migrating-from-modules-development.rst @@ -0,0 +1,212 @@ +Migrating from modules development +================================== + +.. deprecated:: 25.05 + + Developing IOC using NixOS-like modules + +Explanation +----------- + +NixOS-like modules were used to define your IOC, +for example using code such as this: + +.. code-block:: nix + :caption: Deprecated IOC definition + + myEpnixConfig = {pkgs, ...}: { + epnix = { + inherit inputs; + + meta.name = "my-top"; + + support.modules = with pkgs.epnix.support; [StreamDevice]; + + checks.imports = [./checks/simple.nix]; + + nixos.services.ioc = { + app = "example"; + ioc = "iocExample"; + }; + }; + }; + +This style of development is deprecated since EPNix version ``nixos-25.05`` +and will be removed in EPNix version ``nixos-26.05``. + +This style of development was deprecated +because it lead to complex logic inside of EPNix, +and provided no tangible benefit. +Moreover, +support top IOCs are packaged differently inside of EPNix, +in a style much more similar to what you can find in nixpkgs. + +The newer way of developing IOCs is more similar to the Nix code you can find in the wild, +which makes public documentation more applicable to EPNix developments. + +Copying the new template +------------------------ + +From the top directory of your IOC, +move your :file:`flake.nix` file and checks out of the way, +and initialize the new template over your project: + +.. code-block:: bash + :caption: Applying the new template + + mv flake.nix flake.nix.old + mv checks checks.old + nix flake init -t epnix + +Edit the new template +--------------------- + +Flake +^^^^^ + +For every flake input that you added in your :file:`flake.nix.old` file, +add them in your new :file:`flake.nix` file. + +For every overlay that's in your :file:`flake.nix.old`'s ``nixpkgs.overlays`` attribute, +add them in your new :file:`flake.nix` file, +in ``pkgs``' ``overlays``. + +Change the name of your IOC by replacing every instance of ``myIoc`` in :file:`flake.nix`. + +.. warning:: + + If your top is used as an EPICS support top, + your package will be located in a different attribute path. + + For example, + if your package was under ``pkgs.epnix.support.supportTop`` before, + after the migration it will be exported under ``pkgs.supportTop``. + +IOC package +^^^^^^^^^^^ + +Edit the :file:`ioc.nix` file to match your IOC: + +- Change the ``pname``, ``version``, and ``varname`` variables +- Add your EPICS support modules dependencies into ``propagatedBuildInputs`` +- Add your system libraries dependencies into both ``nativeBuildInputs`` and ``buildInputs`` + +If you had :samp:`buildConfig.attrs.{something} = {value};` defined in :file:`flake.nix.old`, +add :samp:`{something} = {value};` to your :file:`ioc.nix` file. + +Checks +^^^^^^ + +For each :file:`checks.old/{check}.nix` file directory, +take the new :file:`checks/simple.nix` as a base and: + +- copy the ``testScript`` from your old check into the new one +- if you made changes to ``nodes`` or ``nodes.machine``, + add them to the new check + +External apps (IEE) +------------------- + +If you defined external apps in :file:`flake.nix.old` such as this: + +.. code-block:: nix + :caption: Deprecated usage of external apps + + application.apps = [ + "inputs.exampleApp" + ]; + +You will need to copy them manually in :file:`ioc.nix`. + + +To do this, +make sure you've re-added :samp:`inputs.{example}App` to your new :file:`flake.nix`, +and pass your ``inputs`` as argument to your IOC: + +.. code-block:: diff + :caption: :file:`flake.nix` + + overlays.default = final: _prev: { + - myIoc = final.callPackage ./ioc.nix {}; + + myIoc = final.callPackage ./ioc.nix { inherit inputs; }; + }; + +.. code-block:: diff + :caption: :file:`ioc.nix` + + { + mkEpicsPackage, + lib, + epnix, + + inputs, + }: + mkEpicsPackage { + pname = "myIoc"; + +Copy your apps manually, +during the ``preConfigure`` phase. +For example, +if you have two apps ``exampleApp`` and ``otherExampleApp``: + +.. code-block:: nix + :caption: :file:`ioc.nix` + :emphasize-lines: 6-11 + + #local_release = { + # PCRE_INCLUDE = "${lib.getDev pcre}/include"; + # PCRE_LIB = "${lib.getLib pcre}/lib"; + #}; + + preConfigure = '' + echo "Copying exampleApp" + cp -rTvf --no-preserve=mode ${inputs.exampleApp} ./exampleApp + echo "Copying otherExampleApp" + cp -rTvf --no-preserve=mode ${inputs.otherExampleApp} ./otherExampleApp + ''; + + meta = { + description = "A description of my IOC"; + homepage = ""; + # ... + }; + +NixOS machines +-------------- + +If you have in a single project both a NixOS configuration and an IOC, +you need to adapt your code to package your IOC outside of NixOS modules. + +The simplest way to do that +is by separating your IOC into a new project, +and follow the migration guide from there. + +Complete example +---------------- + +Here is a complete example of a successful migration. + +Before +^^^^^^ + +.. literalinclude:: ./migrating-from-modules-development/before-flake.nix + :caption: :file:`flake.nix` --- Before + :language: nix + +.. literalinclude:: ./migrating-from-modules-development/before-checks-simple.nix + :caption: :file:`checks/simple.nix` --- Before + :language: nix + +After +^^^^^ + +.. literalinclude:: ./migrating-from-modules-development/after-flake.nix + :caption: :file:`flake.nix` --- After + :language: nix + +.. literalinclude:: ./migrating-from-modules-development/after-ioc.nix + :caption: :file:`flake.nix` --- After + :language: nix + +.. literalinclude:: ./migrating-from-modules-development/after-checks-simple.nix + :caption: :file:`checks/simple.nix` --- After + :language: nix diff --git a/docs/ioc/user-guides/deprecations/migrating-from-modules-development/after-checks-simple.nix b/docs/ioc/user-guides/deprecations/migrating-from-modules-development/after-checks-simple.nix new file mode 100644 index 00000000..0d1b9bdd --- /dev/null +++ b/docs/ioc/user-guides/deprecations/migrating-from-modules-development/after-checks-simple.nix @@ -0,0 +1,40 @@ +{ + nixosTest, + epnix, + epnixLib, + myIoc, + ... +}: +nixosTest { + name = "simple"; + + nodes.machine = { + imports = [epnixLib.inputs.self.nixosModules.nixos]; + environment.systemPackages = [epnix.epics-base]; + + services.iocs.myExampleIoc = { + package = myIoc; + workingDirectory = "iocBoot/iocMyExample"; + }; + }; + + testScript = '' + machine.wait_for_unit("default.target") + machine.wait_for_unit("ioc.service") + + machine.wait_until_succeeds("caget stringin", timeout=10) + machine.wait_until_succeeds("caget stringout", timeout=10) + machine.fail("caget non-existing") + + with subtest("testing stringout"): + def test_stringout(_) -> bool: + machine.succeed("caput stringout 'hello'") + status, _output = machine.execute("caget -t stringout | grep -qxF 'hello'") + + return status == 0 + + retry(test_stringout) + + assert "hello" not in machine.succeed("caget -t stringin") + ''; +} diff --git a/docs/ioc/user-guides/deprecations/migrating-from-modules-development/after-flake.nix b/docs/ioc/user-guides/deprecations/migrating-from-modules-development/after-flake.nix new file mode 100644 index 00000000..277af6e8 --- /dev/null +++ b/docs/ioc/user-guides/deprecations/migrating-from-modules-development/after-flake.nix @@ -0,0 +1,49 @@ +{ + description = "EPICS IOC for migration demonstration purposes"; + + inputs.flake-utils.url = "github:numtide/flake-utils"; + inputs.epnix.url = "github:epics-extensions/epnix/nixos-24.11"; + + inputs.mySupportModule = { + url = "git+ssh://git@my-server.org/me/exampleApp.git"; + inputs.epnix.follows = "epnix"; + }; + + inputs.exampleApp = { + url = "git+ssh://git@my-server.org/me/exampleApp.git"; + flake = false; + }; + + outputs = { + self, + flake-utils, + epnix, + ... + } @ inputs: + # Add your supported systems here. + # --- + # "x86_64-linux" should still be specified so that the development + # environment can be built on your machine. + flake-utils.lib.eachSystem ["x86_64-linux"] (system: let + pkgs = import epnix.inputs.nixpkgs { + inherit system; + overlays = [ + epnix.overlays.default + self.overlays.default + + inputs.mySupportModule.overlays.default + ]; + }; + in { + packages.default = pkgs.myIoc; + + checks = { + simple = pkgs.callPackage ./checks/simple.nix {}; + }; + }) + // { + overlays.default = final: _prev: { + myIoc = final.callPackage ./ioc.nix {inherit inputs;}; + }; + }; +} diff --git a/docs/ioc/user-guides/deprecations/migrating-from-modules-development/after-ioc.nix b/docs/ioc/user-guides/deprecations/migrating-from-modules-development/after-ioc.nix new file mode 100644 index 00000000..7b2c9d59 --- /dev/null +++ b/docs/ioc/user-guides/deprecations/migrating-from-modules-development/after-ioc.nix @@ -0,0 +1,31 @@ +{ + mkEpicsPackage, + epnix, + openssl, + inputs, +}: +mkEpicsPackage { + pname = "myExampleTop"; + version = "0.0.1"; + varname = "MY_EXAMPLE_TOP"; + + src = ./.; + + buildInputs = [openssl]; + nativeBuildInputs = [openssl]; + + propagatedBuildInputs = [ + epnix.support.StreamDevice + epnix.support.mySupportModule + ]; + + preConfigure = '' + echo "Copying exampleApp" + cp -rTvf --no-preserve=mode ${inputs.exampleApp} ./exampleApp + ''; + + meta = { + description = "EPICS IOC for migration demonstration purposes"; + homepage = ""; + }; +} diff --git a/docs/ioc/user-guides/deprecations/migrating-from-modules-development/before-checks-simple.nix b/docs/ioc/user-guides/deprecations/migrating-from-modules-development/before-checks-simple.nix new file mode 100644 index 00000000..f4e343e4 --- /dev/null +++ b/docs/ioc/user-guides/deprecations/migrating-from-modules-development/before-checks-simple.nix @@ -0,0 +1,39 @@ +{ + epnix, + epnixConfig, + pkgs, + ... +}: +pkgs.nixosTest { + name = "simple"; + + nodes.machine = {config, ...}: { + imports = [ + epnix.nixosModules.ioc + epnixConfig + ]; + environment.systemPackages = [pkgs.epnix.epics-base]; + + systemd.services.ioc = config.epnix.nixos.services.ioc.config; + }; + + testScript = '' + machine.wait_for_unit("default.target") + machine.wait_for_unit("ioc.service") + + machine.wait_until_succeeds("caget stringin", timeout=10) + machine.wait_until_succeeds("caget stringout", timeout=10) + machine.fail("caget non-existing") + + with subtest("testing stringout"): + def test_stringout(_) -> bool: + machine.succeed("caput stringout 'hello'") + status, _output = machine.execute("caget -t stringout | grep -qxF 'hello'") + + return status == 0 + + retry(test_stringout) + + assert "hello" not in machine.succeed("caget -t stringin") + ''; +} diff --git a/docs/ioc/user-guides/deprecations/migrating-from-modules-development/before-flake.nix b/docs/ioc/user-guides/deprecations/migrating-from-modules-development/before-flake.nix new file mode 100644 index 00000000..4e96dc20 --- /dev/null +++ b/docs/ioc/user-guides/deprecations/migrating-from-modules-development/before-flake.nix @@ -0,0 +1,82 @@ +{ + description = "EPICS IOC for migration demonstration purposes"; + + inputs.flake-utils.url = "github:numtide/flake-utils"; + inputs.epnix.url = "github:epics-extensions/epnix/nixos-24.11"; + + inputs.mySupportModule = { + url = "git+ssh://git@my-server.org/me/exampleApp.git"; + inputs.epnix.follows = "epnix"; + }; + + inputs.exampleApp = { + url = "git+ssh://git@my-server.org/me/exampleApp.git"; + flake = false; + }; + + outputs = { + self, + flake-utils, + epnix, + ... + } @ inputs: let + myEpnixConfig = {pkgs, ...}: { + nixpkgs.overlays = [inputs.mySupportModule.overlays.default]; + + epnix = { + inherit inputs; + + meta.name = "myExampleTop"; + + support.modules = with pkgs.epnix.support; [StreamDevice mySupportModule]; + applications.apps = ["inputs.exampleApp"]; + + buildConfig.attrs.buildInputs = [pkgs.openssl]; + buildConfig.attrs.nativeBuildInputs = [pkgs.openssl]; + + checks.imports = [./checks/simple.nix]; + + nixos.services.myExampleIoc = { + app = "myExample"; + ioc = "iocMyExample"; + }; + }; + }; + in + # Add your supported systems here. + # --- + # "x86_64-linux" should still be specified so that the development + # environment can be built on your machine. + flake-utils.lib.eachSystem ["x86_64-linux"] (system: let + epnixDistribution = epnix.lib.evalEpnixModules { + nixpkgsConfig = { + # This specifies the build architecture + inherit system; + + # This specifies the host architecture, uncomment for cross-compiling + # + # The complete of example architectures is here: + # https://github.com/NixOS/nixpkgs/blob/nixos-22.11/lib/systems/examples.nix + # --- + #crossSystem = epnix.inputs.nixpkgs.lib.systems.examples.armv7l-hf-multiplatform; + }; + epnixConfig = myEpnixConfig; + }; + in { + packages = + epnixDistribution.outputs + // { + default = self.packages.${system}.build; + }; + + inherit epnixDistribution; + + devShells.default = self.packages.${system}.devShell; + + checks = epnixDistribution.config.epnix.checks.derivations; + }) + // { + overlays.default = final: prev: + self.epnixDistribution.x86_64-linux.generatedOverlay final prev; + }; +} diff --git a/docs/ioc/user-guides/index.rst b/docs/ioc/user-guides/index.rst index 8a5cf175..d0de214c 100644 --- a/docs/ioc/user-guides/index.rst +++ b/docs/ioc/user-guides/index.rst @@ -16,5 +16,6 @@ User guides ./reccaster.rst ./mrf-devices.rst ./testing/index.rst + ./deprecations/index.rst .. TODO: make a NixOS test user-guide diff --git a/docs/release-notes/2505.rst b/docs/release-notes/2505.rst new file mode 100644 index 00000000..12cc3c83 --- /dev/null +++ b/docs/release-notes/2505.rst @@ -0,0 +1,14 @@ +25.05 Release notes +=================== + +.. default-domain:: nix + +.. role:: nix(code) + :language: nix + +Breaking changes +---------------- + +- The old way of developing IOCs was deprecated, + and will be removed in version ``nixos-26.05``. + Please follow the migration guide :doc:`../ioc/user-guides/deprecations/migrating-from-modules-development`.