This document is both some upstream notes and an introduction to
maintaining ddupdate code.


Design goals:
-------------

  - ddupdate should be easy to configure. Most users should just need to
    run a simple configuration script (after registering with a DDNS
    service).
  - ddupdate should be secure, using standard tools to handle password
    secrets. No root access should be required to configure or run it.
  - ddupdate should be extensible, and thus being based on plugins.
  - ddupdate should be flexible for programmers, making it possible to
    write plugins for all sorts of sites including those ddclient cannot
    handle.
  - ddupdate should be linux-centric, using standard linux tools such as
    systemd and NetworkManager where it is appropriate.


Writing plugins
---------------

Writing plugins is not hard. Most plugins are about 10-20 lines of code +
docs, most of which boilerplate stuff. The best way is to look at the
existing plugins and pick solutions from them. Some hints:

  - Before writing the plugin, make tests with wget or curl to make
    sure how the api works. Essential step, this one.

  - The plugin API is defined in the ```ddplugin.py``` file. API docs can
    be generated using *python3 -m pydoc lib/ddupdate/ddplugin.py* or so.

  - Coding style: Use *make pylint*, *make pycodestyle* and *make pydocstyle*.
    The relevant tools python3-pylint, pycodestyle and pydocstyle needs
    to be in place.

  - Each plugin must live in a file with a unique name. It must contain a
    main class derived from AddressPlugin or ServicePlugin. The class
    docstring is the *help <plugin>* documentation.

  - The class ```_name``` property is the official name of the plugin, must
    be unique. ```_oneliner``` is indeed the short summary displayed by
    for example *--list-services*.

  - To test, create the directory *~/.local/share/ddupdate/plugins* and
    drop the new plugin into it.

  - Authentication:
      - Some sites uses standard basic authentication. This is handled
        by *http_basic_auth_setup* in e. g., ```no_ip.py```
      - Others uses username + password in the url e. g., ```dnsexit.py```
      - Hashed passwords are used in e. g., ```dynu.py```
      - API tokens are handled in e. g., ```duckdns.py```
      - Some have broken basic authentication, see ```now_dns.py```
      - Some uses a separate header with the API token, see ```desec.io```

  - Most services uses a http GET request to set the data. See
    ```freedns_io.py``` for a http POST example.

  - Handling expired server certificate: see duiadns.

  - Reply decoding:
      - Most sites just returns some text, simple enough
      - json: example in ```system_ns.py```
      - html: example in ```duiadns.py```

  - Configuration:
      The line 'netrc line' in the plugin method documentation is parsed
      by ddupdate-config to determine what user should define for example
      user, password, etc. This mechanism based on the netrc syntax is
      used also in the keyring backend.


Packaging
---------

ddupdate has a multitude of packaging:

  - ddupdate is available as a **pypi package** from the master branch. It
    can be installed using pip::

        $ sudo pip install ddupdate --prefix=/usr/local

    or from the cloned git directory:

        $ sudo python3 setup.py install --prefix=/usr/local

    It can be installed in an virtualenv root by a regular user. To use
    the plugins in the venv in favor of the system ones prepend the proper
    path to XDG\_DATA\_DIRS using something like::

        $ export  XDG_DATA_DIRS=$PWD/share:$XDG_DATA_DIRS

    Using a virtualenv, configuration files like *~/.config/ddupdate.conf*
    and *~/.netrc* are still used from their system-wide locations.

  - **fedora** is packaged in the *fedora* branch.  Pre-built packages are
    at https://copr.fedorainfracloud.org/coprs/leamas/ddupdate/. Building
    requires the *git* and *rpm-build* packages. To build version 0.7.2::

        $ git clone -b fedora https://github.com/leamas/ddupdate.git
        $ cd ddupdate/fedora
        $ sudo dnf builddep ddupdate.spec
        $ ./make-tarball 0.7.2
        $ rpmbuild -D "_sourcedir $PWD" -ba ddupdate.spec
        $ sudo rpm -U --force rpmbuild/RPMS/noarch/ddupdate*rpm

  - The **debian** packaging is based on gbp and lives in the *debian* and
    *pristine-tar* branches.  The packages *git-buildpackage*, *devscripts*
    and *git*  are required to build. To build current version 0.7.2 do::

        $ rm -rf ddupdate; mkdir ddupdate; cd ddupdate
        $ git clone -o origin -b debian https://github.com/leamas/ddupdate.git
        $ cd ddupdate
        $ sudo mk-build-deps -i -r  debian/control
        $ git fetch origin pristine-tar:pristine-tar
        $ gbp buildpackage --git-upstream-tag=0.7.2 -us -uc
        $ sudo dpkg -i ../ddupdate_0.7.2*_all.deb
        $ git clean -fd             # To be able to rebuild

  - A simpler way to build **debian** packages is based on retreiving the
    sources from the ubuntu ppa and rebuilding them::

        # First-time setup
        $ sudo apt-get install devscripts build-essential
        $ ppa="http://ppa.launchpad.net/leamas-alec/ddupdate/ubuntu"
        $ echo "deb-src $ppa xenial main" | sudo tee -a /etc/apt/sources.list
        $ sudo apt-key \
            adv --keyserver keyserver.ubuntu.com --recv-keys B0319103FF2D1390

        # Rebuild package
        $ sudo apt-get update
        $ apt-get source --build ddupdate
        $ sudo dpkg -i ddupdate*.all.deb


Creating a new version (maintainer work)
----------------------------------------

  - Replace all occurrences of version string:

        sed -E -i 's/([^0-9])0\.[1-9]\.[0-9]/\10.5.1/g' $(git ls-files)

  - Update NEWS file.

  - Check Last Changed in all manpages.

  - Commit and tag the release: git tag 0.7.2

  - Create fedora package:

        git checkout fedora
        cd fedora
        ./make-tarball 0.7.2
        rpmdev-bumpspec *.spec , and edit it.
        rm -rf rpmbuild
        rpmbuild-here -ba *.spec

  - Possibly iterate. When done, merge to master and push with
    tags:
        
        $ git checkout master
        $ git merge devel
        $ git push --tags origin master:master
        $ git push origin devel:devel

  - Copy tarball and repo to debian and commit it on pristine-tar

  - Upload to pypi:

        $ python setup.py sdist
        $ twine upload dist/*

  - Create debian test build on sid::

        $ cd ddupdate/ddupdate
        $ sudo mk-build-deps -i -r  debian/control
        $ git fetch upstream pristine-tar:pristine-tar
        $ git merge -X theirs 0.7.2
        $ dch -v 0.7.2-1
        $ git commit -am "debian: 0.7.2-1"
        $ Check systemd/ddupdate.service
        $ git commit -a --amend
        $ git  clean -fd
        $ gbp buildpackage --git-upstream-tag=0.7.2 -us -uc
        $ git clean -fd    # To be able to rebuild

  - Create fedora packages (above)
  - Make a new COPR build
  - Create an Ubuntu package. Needs tar >= 1.29b, pristine-tar >= 1.42
    (from zesty). Builds on xenial and trusty.

         $ sudo ntpdate se.pool.ntp.org

         # Setup build environment
         $ rm -rf ddupdate; mkdir ddupdate; cd ddupdate
         $ git clone \
             -o upstream -b debian https://github.com/leamas/ddupdate.git
         $ cd ddupdate
         $ git fetch upstream pristine-tar:pristine-tar
         $ pristine-tar checkout ddupdate_0.7.2.orig.tar.gz
         $ mv ddupdate_0.7.2.orig.tar.gz ..
         $ sudo mk-build-deps -i -r debian/control

         # Patch ubuntu stuff:
         $ debian/deb2xenial

         # Build and install the binary package
         $ debuild -us -uc
         $ sudo dpkg -i ../ddupdate_0.7.2_all.deb

         # Build and distribute source package (upstream only)
         $ debuild -S
         $ dput ppa:leamas-alec/ddupdate ../*source.changes
