diff --git a/.github/GitHub-Actions.org b/.github/GitHub-Actions.org
new file mode 100644
index 0000000..1c882b8
--- /dev/null
+++ b/.github/GitHub-Actions.org
@@ -0,0 +1,41 @@
+* Deploying using Github Actions
+
+** Actions definition: workflow/sonatype-deploy.yml
+
+When a GitHub Release is created, usually from a tag, this action will trigger.
+
+Using JDK8 the software will be packaged, including running any tests.
+
+Then the Deploy script will sign the created artifacts then deploy them according to the distributionManagement configuration in the `pom.xml`.
+
+** Deploy Script
+
+Uses a signing key provided from the GitHub Actions Secrets as an environment variable to sign the artifact(s) before they are then deployed.
+
+*** Inputs
+
+**** DEPLOY_PROJECTS (optional)
+
+An optional list of modules in a multi-module project to be deployed. If this value is not specified, then all projects will be deployed.
+
+** Maven Configuration
+
+Picks up the credentials from Environment variables for authenticating both with GPG and with the target deployment server (e.g. sonatype-nexus).
+
+*** Inputs
+
+**** NEXUS_USERNAME
+
+The username for your account on the deployment server.
+
+**** NEXUS_PASSWORD
+
+The password for your account on the deployement server.
+
+**** GPG_KEYNAME
+
+The key to use when signing.
+
+**** GPG_PASSPHRASE
+
+The passphrase to unlock the key to use when signing.
diff --git a/.github/NOTES b/.github/NOTES
new file mode 100644
index 0000000..64253c9
--- /dev/null
+++ b/.github/NOTES
@@ -0,0 +1,53 @@
+Add subkeys:
+
+????
+
+Publish:
+
+gpg --send-keys --keyserver keyserver.ubuntu.com $KEYID
+gpg --send-keys --keyserver pgp.mit.edu $KEYID
+gpg --send-keys --keyserver pool.sks-keyservers.net $KEYID
+
+Backup:
+
+gpg --export --armor pcampbell@kemitix.net > gpg-key-backup.asc
+gpg --export-secret-keys --armor pcampbell@kemitix.net >> gpg-key-backup.asc
+
+Export sub-keys:
+
+gpg --export-secret-subkeys pcampbell@kemitix.net > subkeys
+
+Remove master keys:
+
+gpg --delete-secret-key pcampbell@kemitix.net
+
+Import sub-keys and clean up:
+
+gpg --import subkeys
+
+shred --remove subkeys
+
+Delete any encryption subkeys:
+
+gpg --edit-key pcampbell@kemitix.net
+
+2
+delkey
+save
+
+Change passphrase:
+
+gpg --edit-key pcampbell@kemitix.net
+passwd
+save
+
+Export keys:
+
+gpg --export --armor pcampbell@kemitix.net > codesigning.asc
+gpg --export-secret-keys --armor pcampbell@kemitix.net >> codesigning.asc
+
+Encrypt keys:
+
+gpg --symmetric --cipher-algo AES256 codesigning.asc
+
+shred codesigning.asc
diff --git a/.github/codesigning.asc.gpg b/.github/codesigning.asc.gpg
new file mode 100644
index 0000000..f5c71e0
Binary files /dev/null and b/.github/codesigning.asc.gpg differ
diff --git a/.github/settings.xml b/.github/settings.xml
new file mode 100644
index 0000000..8791e47
--- /dev/null
+++ b/.github/settings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ sonatype-nexus-snapshots
+ ${env.NEXUS_USERNAME}
+ ${env.NEXUS_PASSWORD}
+
+
+ sonatype-nexus-staging
+ ${env.NEXUS_USERNAME}
+ ${env.NEXUS_PASSWORD}
+
+
+
+
+ gpg-sign
+
+ true
+
+
+ gpg
+ ${env.GPG_KEYNAME}
+ ${env.GPG_PASSPHRASE}
+
+
+
+
diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml
new file mode 100644
index 0000000..470baeb
--- /dev/null
+++ b/.github/workflows/maven-build.yml
@@ -0,0 +1,25 @@
+# This workflow will build a Java project with Maven
+# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
+
+name: Java CI with Maven
+
+on:
+ push:
+ branches: '*'
+ pull_request:
+ branches: '*'
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ java: [ 8, 11, 13 ]
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up JDK ${{ matrix.java }}
+ uses: actions/setup-java@v1
+ with:
+ java-version: ${{ matrix.java }}
+ - name: Build with Maven
+ run: mvn -B install
diff --git a/.github/workflows/sonatype-deploy.yml b/.github/workflows/sonatype-deploy.yml
new file mode 100644
index 0000000..63b14fe
--- /dev/null
+++ b/.github/workflows/sonatype-deploy.yml
@@ -0,0 +1,38 @@
+name: Deploy to Sonatype Nexus
+
+on:
+ release:
+ types: [created]
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up JDK 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ - name: Build with Maven
+ run: mvn -B install
+ - name: Nexus Repo Publish
+ run: |
+ gpg --quiet \
+ --batch \
+ --yes \
+ --decrypt \
+ --passphrase="${{ secrets.GPG_PASSPHRASE }}" \
+ --output codesigning.asc \
+ .github/codesigning.asc.gpg
+ gpg --batch \
+ --fast-import codesigning.asc
+ mvn --settings .github/settings.xml \
+ -Dskip-Tests=true \
+ -P release \
+ -B \
+ deploy
+ env:
+ NEXUS_USERNAME: ${{ secrets.NEXUS_USERNAME }}
+ NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}
+ GPG_KEYNAME: ${{ secrets.GPG_KEYNAME }}
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8bcd005
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,16 @@
+*.jqwik-database*
+
+*.iml
+.idea/
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+
+# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
+!/.mvn/wrapper/maven-wrapper.jar
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8c41a89
--- /dev/null
+++ b/README.md
@@ -0,0 +1,37 @@
+# List Head/Tail
+
+![Sonatype Nexus (Release)](https://img.shields.io/nexus/r/https/oss.sonatype.org/net.kemitix/list-head-tail.svg?style=for-the-badge)
+![Maven Central](https://img.shields.io/maven-central/v/net.kemitix/list-head-tail.svg?style=for-the-badge)
+
+Provides a `head()` and `tail()` implementation for `List<>` objects in Java.
+
+## Usage
+
+```java
+import net.kemitix.list.HeadTail;
+import java.util.List;
+import static org.assertj.core.api.Assertions.assertThat;
+class Usage {
+ List list = Arrays.asList("a", "b", "c");
+ Optional head = HeadTail.head(list); // head()
+ assertThat(head).contains("a");
+ List tail = HeadTail.tail(list); // tail()
+ assertThat(tail).containsExactly("b", "c");
+}
+```
+
+Statically importing the `head()` and `tail()` methods will be more
+readable and idiomatically FP.
+
+```java
+import static net.kemitix.list.HeadTail.*;
+import java.util.List;
+import static org.assertj.core.api.Assertions.assertThat;
+class Usage {
+ List list = Arrays.asList("a", "b", "c");
+ Optional head = head(list); // head()
+ assertThat(head).contains("a");
+ List tail = tail(list); // tail()
+ assertThat(tail).containsExactly("b", "c");
+}
+```
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..864ed78
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,59 @@
+
+
+ 4.0.0
+
+
+ net.kemitix
+ kemitix-parent
+ 5.3.0
+
+
+
+ list-head-tail
+ DEV-SNAPSHOT
+ 2020
+
+
+ 1.8
+ ${java.version}
+ ${java.version}
+ 2.16
+ 2.5.0
+ 5.4.0
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.6.1
+ test
+
+
+ org.assertj
+ assertj-core
+ 3.15.0
+ test
+
+
+
+
+
+
+ io.repaint.maven
+ tiles-maven-plugin
+ ${tiles-maven-plugin.version}
+ true
+
+
+ net.kemitix.tiles:all:${kemitix-maven-tiles.version}
+ net.kemitix.checkstyle:tile:${kemitix-checkstyle-ruleset.version}
+
+
+
+
+
+
+
diff --git a/src/main/java/net/kemitix/list/HeadTail.java b/src/main/java/net/kemitix/list/HeadTail.java
new file mode 100644
index 0000000..90b7f24
--- /dev/null
+++ b/src/main/java/net/kemitix/list/HeadTail.java
@@ -0,0 +1,62 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Paul Campbell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package net.kemitix.list;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Provides a simple implementation of head() and tail() for {@link List}s.
+ */
+public interface HeadTail {
+
+ /**
+ * Returns the first item in the list as an {@link Optional}.
+ *
+ * @param list the list
+ * @param the type of the lists contents
+ * @return an Optional containing the first item in the list, or empty if
+ * the list is empty.
+ */
+ static Optional head(final List list) {
+ if (list.isEmpty()) {
+ return Optional.empty();
+ }
+ return Optional.ofNullable(list.get(0));
+ }
+
+ /**
+ * Returns the list, minus the first item.
+ *
+ * @param list the list
+ * @param the type of the lists contents
+ * @return a view of the list starting with the second item, or an empty
+ * list if the original list has less than two items.
+ */
+ static List tail(final List list) {
+ if (list.size() < 1) {
+ return Collections.emptyList();
+ }
+ return list.subList(1, list.size());
+ }
+}
diff --git a/src/test/java/net/kemitix/list/HeadTailTest.java b/src/test/java/net/kemitix/list/HeadTailTest.java
new file mode 100644
index 0000000..474b6a1
--- /dev/null
+++ b/src/test/java/net/kemitix/list/HeadTailTest.java
@@ -0,0 +1,51 @@
+package net.kemitix.list;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class HeadTailTest {
+
+ private String headValue = "1-" + UUID.randomUUID().toString();
+ private String secondValue = "2-" + UUID.randomUUID().toString();
+ private String thirdValue = "3-" + UUID.randomUUID().toString();
+ private List emptyList = Collections.emptyList();
+ private List singletonList = Collections.singletonList(headValue);
+ private List aList = Arrays.asList(
+ headValue, secondValue, thirdValue
+ );
+ private List tailValue = Arrays.asList(
+ secondValue, thirdValue
+ );
+
+ @Test
+ public void headOfAnEmptyListIsEmpty() {
+ assertThat(HeadTail.head(emptyList)).isEmpty();
+ }
+ @Test
+ public void headOfASingletonListIsTheItem() {
+ assertThat(HeadTail.head(singletonList)).contains(headValue);
+ }
+ @Test
+ public void headOfAListIsTheFirstItem() {
+ assertThat(HeadTail.head(aList)).contains(headValue);
+ }
+
+ @Test
+ public void tailOfAnEmptyListIsEmpty() {
+ assertThat(HeadTail.tail(emptyList)).isEmpty();
+ }
+ @Test
+ public void tailOfASingletonListIsEmpty() {
+ assertThat(HeadTail.tail(singletonList)).isEmpty();
+ }
+ @Test
+ public void tailOfAListIsMinusTheHead() {
+ assertThat(HeadTail.tail(aList)).isEqualTo(tailValue);
+ }
+}
\ No newline at end of file