Svnman
Svnman (pronounced ess-vee-en-man) is the ProgClub Subversion project management software. That's the software that manages creation, maintenance, and release of Subversion projects. For other projects see projects.
Status
We use semantic versioning. Latest production version: 1.0. Latest development version: 1.0.
See tasks for work that still needs to be done.
Motivation
Why this software? Basically to help with project management, particularly around versioning of artefacts.
Administration
Contributors
Members who have contributed to this project. Newest on top.
All contributors have agreed to the terms of the Contributor License Agreement. This excludes any upstream contributors who tend to have different administrative frameworks.
Copyright
Copyright © 2020, Contributors.
License
Licensed under the MIT license.
Components
Libraries, tools, services or media from third parties used under license:
Resources
Downloads
There are presently no tarballs.
Source code
The source code can be browsed online:
The most interesting code is here:
The latest stable (read-only) released version of the code is available from Subversion here:
Or if you want the latest version for development purposes:
Note that our software development is done on the version branch, not on trunk. We use trunk to track the latest stable release.
Links
- Version Control with Subversion (svnbook)
Specifications
Versioning
The versioning article goes into more detail, but basically the version numbers supported by svnman are in the format MAJOR.MINOR.PATCH. Note that this versioning standard is used by both the svnman software itself and also any projects which use svnman as their project administration tool. This versioning standard is the only versioning standard supported by svnman at the present time. Note that there can be multiple MAJOR.MINOR releases whereas a specific MAJOR.MINOR.PATCH release is unique. This means that you can continue issuing patches (such as security updates) to particular MAJOR.MINOR versions and releasing them. If you need a new MAJOR.MINOR version (say for your next prospective production release) then see the bump-minor subcommand for help with that. If you're getting ready for a new MAJOR version (which you should definitely consider if you're planning to introduce breaking changes) then see bump-major. The versioning specification has support for extra version info, but that is not relevant to svnman, and svnman can be used with or without that extra information.
Version component | Description | Details |
---|---|---|
MAJOR | major version number | incremented for breaking changes, except for version zero which may be unstable |
MINOR | minor version number | incremented for non-breaking changes for new supported versions |
PATCH | patch version number | incremented prior to every commit (and release); the PATCH is odd for DEV builds and even for PROD builds |
Builds
Only two types of builds are supported by svnman: DEV and PROD. If you want to cut a BETA or Release Candidate (RC1, RC2, etc.) build, you can do that (see the versioning article for help on this topic), but it's best to use a DEV build for those, saving PROD for real live actual supported versions. When you run a release with the `svnman release` subcommand a PROD build is configured for release. After the production release the project is reconfigured as the next DEV build. So PROD builds only exist during a release, and before and after a release the project is for a DEV build. In this way all releases are releases to PROD, that's what 'release' means.
DEV
A 'DEV' (for "development") build is for a programmer or tester for testing and development purposes. You can use a DEV build in production if you want, but it might have known errors, change rapidly, or otherwise be unsupported. You can tell DEV builds by their odd PATCH version number (PATCH % 2 == 1). If you track the version branch you will typically see a DEV build, except for temporarily during an `svnman release`.
PROD
A 'PROD' (for "production") build is created by a project administrator (using the `svnman release` subcommand) when testing of the version branch is complete. You can tell PROD builds by their even PATCH version number (PATCH % 2 == 0). During a release PROD builds get tagged with their MAJOR.MINOR version number under 'tags/latest/' and also with their MAJOR/MINOR/PATCH version number under 'tags/release/'. Usually when the latest version branch is released to PROD we update 'trunk' to point at it. In this way 'trunk' usually tracks the latest stable production version, which might be different to what you're used to. If you track 'trunk' or 'tags' you should only ever see PROD builds.
Functional specification
The functional specification describes what the project does.
The software provides a system command called `svnman` which has various subcommands for administering projects in a Subversion repository. For details see the command-line interface.
Technical specification
The technical specification describes how the project works.
The software is written in PHP (tested on version 7.2) and shells out to the `svn` command-line utility (tested on version 1.9.7).
Configuration data is kept in JSON format in $HOME/.config/svnman/config.json
. Use the `svnman config` subcommand to specify configuration data via the command-line.
Command-line interface
general usage: svnman SUBCOMMAND [ARG...] [DIR...] A Subversion project management tool. Type `svnman help <subcommand>` for help on a specific subcommand. Type `svnman version` to see the program version. Type `svnman errors` to see information about possible program errors.
- find us on the web: https://www.progclub.org/wiki/svnman
Global arguments:
-q [--quiet] .........: Suppress error/warning/debug output.
SUBCOMMAND
Available subcommands:
- config
- create
- maint
- commit (or ci)
- freeze
- unfreeze (or thaw)
- release
- bump-minor
- bump-major
- browse
- gen-doc
- gen-vars
- fix-version
Argument formats
ARG
An argument is a name prefixed with two dashes, optionally followed by whitespace and a value, depending on the argument. For example:
--no-trunk
Or:
--repo pcrepo
The special arguemnt '--' indicates that all subsequent command-line values are to be treaded as DIRs not as ARGs.
Note that some arguments also support a shorter version, which is usually a dash followed by a single letter. For example --message can be abbreviated as -m.
DIR
A PATH to a directory in the local file system. Absolute and relative paths are supported for input, but if directory paths are stored (such as with working base directories in repository aliases) the absolute paths are computed and stored at the time of configuration not at time of later use.
Note that if no DIRs are provided the current working directory is processed.
PATH
A location in the local file system. Absolute or relative paths are supported. A path can be to a standard file, directory, symlink, etc. Generally if a directory is required we explicitly document it as a DIR. If your PATH is to the wrong type of file per your SUBCOMMAND/ARG then you can expect to see an error as a result.
URL
A Uniform Resource Locator, typically with a scheme such as:
- http:
- https:
- file:
- svn:
NAME
Name format:
/^[a-z][a-z0-9\._-]{0,42}[a-z0-9]$/
CODE
Code format:
/^[a-z][a-z0-9-]{0,14}[a-z0-9]$/
CONST
Const format:
/^[A-Z][A-Z0-9_]{0,14}[A-Z0-9]$/
ALIAS
Alias format:
/^[a-z][a-z0-9-]{0,14}[a-z0-9]$/
STRING
String format:
/^[^\x00-\x09\x0b\x0c\x0e-\x1f\x7f]*$/
Subcommands
config
config: usage: svnman config ARG...
Configure an svnman repository alias.
A repository alias associates an alias with a repo location, allowing the repo alias to be used as a shortcut for the repo location in commands which accept a --repo argument. A default working base DIR and a ViewVC URL can also be associated with a repo alias.
Note that the config file is in JSON format in:
$HOME/.config/svnman/config.json
If your alias is not already configured then --location is required. If it is already configured the existing location will be retained if a new location is not nominated. If you want to clear an optional setting you can set it to the empty string ().
Required arguments:
--alias ALIAS ........: name of a repository alias
Optional arguments:
--location URL .......: location of the repository --working-base DIR ...: parent directory of the working copy; if unspecified loaded from config file (if possible); if unspecified and no config the current working directory is used --viewvc URL .........: ViewVC URL for the repository
Global arguments:
-q [--quiet] .........: Suppress error/warning/debug output.
For help on argument formats see `svnman help`.
create
create: usage: svnman create ARG...
Create a new project in a Subversion repository.
New projects are created with:
MAJOR version = 0 MINOR version = 1 PATCH version = 1
The odd PATCH version indicates that the initial version is a DEV version.
So basically a new project is just a couple of standard directories in the repository along with an initial version file. Also, if you've included the PHPBOM library, the svn:externals for that will be configured too.
Depending on your command-line options the initial 'branches/0.1' version will be checked out into a working copy for you.
Required arguments:
--project-name NAME ..: name of the project --repo ALIAS|URL .....: Subversion repository to operate on
Optional arguments:
--project-code CODE ..: project code (lowercase); generated from --project-name if unspecified --project-const CONST : project const (uppercase); generated from --project-code if unspecified --working-base DIR ...: parent directory of the working copy; if unspecified loaded from config file (if possible); if unspecified and no config the current working directory is used --working-copy DIR ...: directory containing the working copy; will go under --working-base (using project name) if not specified --checkout ...........: checkout into working copy [default] --no-checkout ........: don't checkout into working copy --phpbom .............: configure svn:externals on 'lib' DIR for PHPBOM library --no-phpbom ..........: don't configure the PHPBOM library [default]
Global arguments:
-q [--quiet] .........: Suppress error/warning/debug output.
For help on argument formats see `svnman help`.
maint
maint: usage: svnman maint [DIR...]
Run maintenance on DIR.
Maintenance includes optional code generation and updating of the PATCH version. You should run this before your commits. You can only run maintenance on DEV builds and this is enforced by this subcommand. If your working copy is for a PROD build (i.e. has an even PATCH version number) then you may need to run `svnman fix-version` to remedy the situation.
For the code generation the file 'bin/dev/gen.sh' is executed, if it exists and is executable.
Global arguments:
-q [--quiet] .........: Suppress error/warning/debug output.
For help on argument formats see `svnman help`.
commit
commit: usage: svnman commit|ci [DIR...]
Run maintenance on DIR then commit.
See `svnman help maint` for information concerning the maintenance process. After maintenance (assuming it is successful) an `svn commit` is performed.
Note that if you are running `svn commit` (AKA: `svn ci`) directly then you should make sure you call `svnman maint` to run maintenance *before* an actual commit. If you don't do this the PATCH version number will not be incremented properly. If you absolutely must run `svn commit` directly then make sure you have incremented the PATCH version by two (to an ODD number) in the version file for your project prior to commit. Run `svnman version` if you need to know which file is being used for version info in your current working directory.
Optional arguments:
-m [--message] STRING : `svn commit` message
Global arguments:
-q [--quiet] .........: Suppress error/warning/debug output.
For help on argument formats see `svnman help`.
freeze
freeze: usage: svnman freeze [DIR...]
Freeze external subprojects in DIR.
Only available for projects that are in 'branches'. When fronzen externals are pinned to the revision which was current at the time of freezing.
Global arguments:
-q [--quiet] .........: Suppress error/warning/debug output.
For help on argument formats see `svnman help`.
unfreeze
unfreeze: usage: svnman unfreeze|thaw [DIR...]
Unfreeze external subprojects in DIR.
Only available for projects that are in 'branches'. When unfronzen externals are unpinned from a specific revision.
Global arguments:
-q [--quiet] .........: Suppress error/warning/debug output.
For help on argument formats see `svnman help`.
release
release: usage: svnman release [ARG...] [DIR...]
Release a project in DIR.
Prior to release projects are frozen, and after release they are unfrozen. When a release is done the starting version must be a DEV build (with an odd PATCH version number), then during the release the PATCH version number will be incremented to an even version number for the release, and after the release the PATCH version number will be incremented again to a new odd version number for use by the next DEV build.
So the release process is roughly:
- freeze
- bump PATCH to next PROD version
- commit
- create tags
- optionally update trunk
- unfreeze
- bump PATCH to next DEV version
- commit
Optional arguments:
--trunk ..............: trunk is updated --no-trunk ...........: trunk is not updated --auto-trunk .........: trunk updated if project is on latest branch [default]
Global arguments:
-q [--quiet] .........: Suppress error/warning/debug output.
For help on argument formats see `svnman help`.
bump-minor
bump-minor: usage: svnman bump-minor [DIR...]
Create a new version branch from DIR, incrementing the MINOR version number.
Note that during a MINOR version bump the PATCH version is preserved. This means that across MAJOR.MINOR versions PATCH version numbers will duplicate and diverge. This is by design. One consequence of this is that the PATCH number indicates, for every MAJOR.MINOR version, pretty much exactly how many commits have been made in that version's entire history.
Note also that the MINOR version number will be updated in both the URL for the branch and in the project version file (so that's in two places).
Global arguments:
-q [--quiet] .........: Suppress error/warning/debug output.
For help on argument formats see `svnman help`.
bump-major
bump-major: usage: svnman bump-major [DIR...]
Create a new version branch from DIR, incrementing the MAJOR version number (and resetting the MINOR version number to zero).
Note that during a MAJOR version bump the PATCH version is preserved. This means that across MAJOR.MINOR versions PATCH version numbers will duplicate and diverge. This is by design. One consequence of this is that the PATCH number indicates, for every MAJOR.MINOR version, pretty much exactly how many commits have been made in that version's entire history.
Note also that the MAJOR version number will be updated in both the URL for the branch and in the project version file (so that's in two places).
Global arguments:
-q [--quiet] .........: Suppress error/warning/debug output.
For help on argument formats see `svnman help`.
browse
browse: usage: svnman browse [DIR...]
Open ViewVC in web browser for project in DIR.
This requires ViewVC to be configured via `svnman config`.
// 2020-02-27 jj5 - NOTE: this functionality hasn't been implemented yet!
Global arguments:
-q [--quiet] .........: Suppress error/warning/debug output.
For help on argument formats see `svnman help`.
gen-doc
gen-doc: usage: svnman gen-doc
Generates svnman documentation for use in the project wiki.
You can find said documentation here:
Have a look in the 'bin/dev/gen.sh' script to see how documentation generation is done.
Global arguments:
-q [--quiet] .........: Suppress error/warning/debug output.
For help on argument formats see `svnman help`.
gen-vars
gen-vars: usage: svnman gen-vars
Generates shell config containing error constants definitions.
You can `source` the output of this command to configure your environment with error info:
`source <(svnman gen-vars)`
Global arguments:
-q [--quiet] .........: Suppress error/warning/debug output.
For help on argument formats see `svnman help`.
fix-version
fix-version: usage: svnman fix-version [DIR...]
If the PATCH version number is left on an even value (perhaps due to a failure during `svnman release`) then this subcommand will bump it to the next odd value, making it a valid development PATCH version number again. If the PATCH version number is already an odd value this subcommand does nothing and fails indicating error.
For more info on our version numbers see:
Global arguments:
-q [--quiet] .........: Suppress error/warning/debug output.
For help on argument formats see `svnman help`.
Errors
Potential svnman error levels and their meaning:
Error level | Error name | Error text | Hint |
---|---|---|---|
0 | ERR_SUCCESS | program completed successfully. | |
10 | ERR_HELP | help (or version/error info) requested. | |
11 | ERR_PHP_ERROR | PHP error. | please let the maintainer know: John Elliot V <jj5@progclub.org> |
12 | ERR_PHP_EXCEPTION | unhandled exception. | please let the maintainer know: John Elliot V <jj5@progclub.org> |
13 | ERR_PHP_ASSERT | assertion failed. | please let the maintainer know: John Elliot V <jj5@progclub.org> |
14 | ERR_NO_ASSERTIONS | "zend.assertions" are not enabled. | enable "zend.assertions" in php.ini. |
15 | ERR_NOT_IMPLEMENTED | functionality not implemented. | |
16 | ERR_NOT_SUPPORTED | situation not supported. | please let the maintainer know: John Elliot V <jj5@progclub.org> |
17 | ERR_SCRIPT_PATH | conflicting SCRIPT_PATHs registered. | please let the maintainer know: John Elliot V <jj5@progclub.org> |
18 | ERR_NOT_A_DIRECTORY | not a directory. | |
19 | ERR_NOT_A_DEV_BUILD | not a DEV build. | this is a PROD build. For a DEV build increment the PATCH by 1. Try `svnman fix-version`. |
20 | ERR_INVALID_PATH | invalid path. | |
21 | ERR_INVALID_BRANCH_VERSION | invalid branch version. | make sure VERSION in branches/VERSION is in format MAJOR.MINOR. |
22 | ERR_INVALID_VERSION | version discrepancy. | check the branches/VERSION is same as MAJOR/MINOR from version file. |
23 | ERR_INVALID_VERSION_PART | invalid version part. | |
24 | ERR_INVALID_WORKING_BASE | invalid working base. | |
25 | ERR_INVALID_EXTERNALS | invalid svn:externals. | |
26 | ERR_INVALID_EXTERNALS_DIR | invalid svn:externals directory. | the directory should come first. |
27 | ERR_INVALID_NAME | invalid name. | |
28 | ERR_INVALID_CODE | invalid code. | |
29 | ERR_INVALID_CONST | invalid const. | |
30 | ERR_INVALID_ALIAS | invalid repository alias. | |
31 | ERR_INVALID_STRING | invalid string. | |
32 | ERR_INVALID_VERSION_PATCH | invalid version PATCH. | |
33 | ERR_INVALID_CONFIG | invalid config. | your config file is corrupt, consider deleting it or revise. |
34 | ERR_MISSING_HOME_ENV_VAR | missing $HOME environment variable. | review your shell configuration such as .bashrc. |
35 | ERR_MISSING_ARG_VALUE | missing argument value. | |
36 | ERR_MISSING_ALIAS | missing --alias. | |
37 | ERR_MISSING_LOCATION | missing --location. | |
38 | ERR_MISSING_HOME_DIR | missing $HOME directory. | |
39 | ERR_MISSING_VERSION_FILE | missing version file. | |
40 | ERR_MISSING_VERSION_PART | missing version part. | |
41 | ERR_MISSING_PROJECT_NAME | missing project name. | |
42 | ERR_VERSION_CONFLICT_MAJOR | MAJOR version conflict. | check the branches/VERSION indicates same as MAJOR in version file. |
43 | ERR_VERSION_CONFLICT_MINOR | MINOR version conflict. | check the branches/VERSION indicates same as MINOR in version file. |
44 | ERR_PATCH_VERSION_EVEN | PATCH version is even (PROD). | consider running `svnman fix-version` to resolve. |
45 | ERR_PATCH_VERSION_ODD | PATCH version is odd (DEV). | if PATCH version is odd there's no need to run `svnman fix-version`. |
46 | ERR_EXPECTED_BRANCHES | expected "branches". | |
47 | ERR_CHDIR_FAILED | chdir failed. | |
48 | ERR_NO_EXTERNALS | no externals. | |
49 | ERR_NOT_WORKING_COPY | not an svn working copy. | |
50 | ERR_CANNOT_MKDIR | cannot mkdir. | |
51 | ERR_CANNOT_MAKE_TEMP_DIR | cannot make temp dir. | |
52 | ERR_CANNOT_MATCH_VERSION_PATCH | cannot match version PATCH. | |
53 | ERR_SVN_MISSING | no executable `svn` command. | |
54 | ERR_SVN_COMMAND_FAILED | svn command failed. | |
55 | ERR_SVN_CANNOT_ACCESS | cannot access svn repository. | |
56 | ERR_SVN_INVALID_INFO | unsupported `svn info` output. | please let the maintainer know: John Elliot V <jj5@progclub.org> |
57 | ERR_SVN_UNEXPECTED_OUTPUT | unexpected svn output. | please let the maintainer know: John Elliot V <jj5@progclub.org> |
58 | ERR_SVN_HAS_CHANGES | found uncommitted changes. | |
59 | ERR_SVN_BRANCH_EXISTS | branch already exists. | |
60 | ERR_FILE_WRITE | error writing file. | |
61 | ERR_FILE_CLOSE | error closing file. | |
62 | ERR_FILE_UNLINK | error unlinking file. | |
63 | ERR_FILE_MISSING | file missing. | |
64 | ERR_CODE_GEN_NOT_EXECUTABLE | code gen script not executable. | you may need to run `chmod +x` on your code gen script. |
65 | ERR_CODE_GEN_FAILED | code generation failed. | |
66 | ERR_CONFIG_WRITE_FAILED | error writing config file. | |
67 | ERR_UNSUPPORTED_FILE_TYPE | unsupported file type. |
Miscellanea
Subversion
The svnman software makes use of the Subversion command-line interface. If Subversion is not installed then svnman won't work!
ViewVC
The ViewVC software can be used for browsing a Subversion repository via the web. We intend to support ViewVC integration, but, ah, that isn't done yet...
PHP
The svnman software is written mostly in the PHP programming language which we^H^HI love! :)
Other bits and pieces are done with JSON, Subversion, and BASH.
Ubuntu
This software was developed and tested on Ubuntu 18.04 LTS.
JSON
We use the JavaScript Object Notation format for our configuration data.
BASH
The bash shell is our Unix shell of choice. You don't need Bash to run svnman but it is used by our build scripts.
$PATH
When you install svnman you should make sure a symlink to bin/svnman.php
is in your $PATH environment variable.
$HOME
We rely on the $HOME environment variable being set to indicate your home directory. We need to know where your home directory is so that we can save and load your configuration data (see config).
PHPBOM
Our svnman utility has first-class support for our PHPBOM library. Basically when you use svnman to create a new project you can optionally have the PHPBOM library automatically included in your project.
Bugslist
We use Bugslist to generate our TODO list.
VERSION
Supported version numbers are in the format MAJOR.MINOR.PATCH. See versioning for more info.
SCRIPT_PATH
Internally in the PHP code we register the absolute path of the running PHP script (usually bin/svnman.php
) in a SCRIPT_PATH constant. Ordinarily users shouldn't need to know about this, except if there's an implementation error which results in an error report concerning the constant.
NOTE
We often put notes (and TODO items etc) in our code. Generally the format is:
// {date} {user} - NOTE: {note}
Where {date} is in ISO 8601 format and the {user} is the username of the person who left the note. A {note} is just some text that aims to be helpful and usually relates to some code that follows the note itself.
Notes
Notes for implementers
If you are interested in incorporating this software into your project, here's what you need to know.
Repository layout
This software presumes that the Subversion project has the typical base directories:
- trunk
- branches
- tags
However, we don't use 'trunk' for development. Instead development happens in MAJOR.MINOR version branches. So version 0.3 is developed in 'branches/0.3' and version 1.0 is developed in 'branches/1.0'. When a version branch is released by this script 'trunk' can optionally be updated. Within tags the latest MAJOR.MINOR versions are maintained under 'tags/latest/MAJOR.MINOR' and releases are tagged under 'tags/release/MAJOR/MINOR/PATCH'.
Installation
In order to run svnman you should create an svnman
symlink somewhere in your $PATH and point it to bin/svnman.php
. So a full installation might look something like this:
$ cd ~/software $ svn co https://www.progclub.org/pcrepo/svnman/trunk svnman $ cd ~/bin $ ln -s ~/software/svnman/bin/svnman.php svnman
You might need to install some dependencies, such as:
# apt install php-cli subversion
This software has been developed and tested on Ubuntu 18.04 LTS GNU/Linux. It will probably work in other Unix environments, and it will definitely be broken on Windows.
Notes for developers
If you're looking to set up a development environment for this project here's what you need to know:
$ svn co https://www.progclub.org/pcrepo/svnman/branches/1.0 svnman-1.0
Notes for ProgClub administrators
To release a version of this project use the software itself.
First, run maintenance and commit any changes:
$ svnman commit
Then run the release:
$ bin/dev/release.sh
You will be prompted to update the project documentation.
Tasks
TODO
MEDIUM
- TODO: bin/dev/gen.sh: 79: generate tasks.wiki with TODO and Done sections...
- TODO: inc/lib.php: 739: this spec should probably be moved into the Settings class and be used for input validation.
- TODO: inc/lib.php: 3586: this needs better reporting.
- TODO: inc/lib.php: 3588: need to support other version file formats. Especially *.cfg and maybe *.ini formats. Note that *.cfg file format is a key value format that is compatible with Unix shell environment config.
- TODO: inc/lib.php: 3592: add a --debug command-line option. Be less verbose if it's not specified.
- TODO: inc/lib.php: 3595: add a 'latest-branch' subcommand that reports the current branch and the latest branch (and if they're the same or different).
- TODO: inc/lib.php: 3603: add support for 'browse' subcommand.
- TODO: inc/lib.php: 3605: clean up after ourselves, delete temp files and directories...
- TODO: inc/lib.php: 3608: add support for --tarball generation during release.
- TODO: inc/lib.php: 3613: add a --json argument which causes program output to be in stable JSON format for better software integration options.
- TODO: inc/lib.php: 3619: in gen-vars subcommand support --prefix option for putting env var names in a namespace...
- TODO: inc/lib.php: 3626: add --error ERROR_CODE|ERROR_NAME option to `svnman errors` which will print out detailled error information for the particular error indicated.
- TODO: inc/lib.php: 3630: add a --bug BUG_NUMBER argument. The BUG_NUMBER to be included in commit messages.
- TODO: inc/lib.php: 3633: when updating version file, don't just update a single file, but update all version files, allowing for there to be multiple version file formats used in a single project. This will entail verifying the all version files agree with each other as regards the version number.
LOW
- THINK: inc/lib.php: 2907: do we want to just automatically mkdir -p the working base..?
- THINK: inc/lib.php: 3599: do we want an optional argument --version-file for manually nominating the version file..? (At the moment we prefer convention over configuration.)
- THINK: inc/lib.php: 3616: consider adding an interactive mode that will confirm options or ask questions.
- THINK: inc/lib.php: 3622: if we're gonna allow refactoring of error name constants we might want a compatibility thing that aliases old/deprecated names to new names. But such a facility isn't necessary yet.
Done
Stuff that's done. Latest stuff on top.