From 18ab421dc0626bca1d81820118ee45f1875a840f Mon Sep 17 00:00:00 2001
From: b6k <39600846+b6k-dev@users.noreply.github.com>
Date: Sat, 14 Mar 2026 11:24:02 +0100
Subject: [PATCH 1/8] - fix package coordinates - enhance pom with repository
metadata
---
pom.xml | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 08ca6d3..580db38 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,9 +4,18 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- dev.b6k
+ io.github.b6k-dev
outcome
- 1.0-SNAPSHOT
+ 1.0.0-SNAPSHOT
+ Outcome
+ Functional Result type for Java
+ https://github.com/b6k-dev/outcome
+
+
+ scm:git:git://github.com/b6k-dev/outcome.git
+ scm:git:ssh://git@github.com/b6k-dev/outcome.git
+ https://github.com/b6k-dev/outcome
+
25
From 2b01665fe7ebdaee887329ecd463a1e75f93eaa7 Mon Sep 17 00:00:00 2001
From: b6k <39600846+b6k-dev@users.noreply.github.com>
Date: Sat, 14 Mar 2026 11:28:21 +0100
Subject: [PATCH 2/8] add license and maven metadata
---
LICENSE | 21 +++++++++++++++++++++
pom.xml | 21 +++++++++++++++++++--
2 files changed, 40 insertions(+), 2 deletions(-)
create mode 100644 LICENSE
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..fe27a60
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 Maciej Bartczak
+
+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.
diff --git a/pom.xml b/pom.xml
index 580db38..7086353 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,13 +8,30 @@
outcome
1.0.0-SNAPSHOT
Outcome
- Functional Result type for Java
+ Lightweight Result type for Java with Ok, Err, and Panic
https://github.com/b6k-dev/outcome
+
+
+ MIT License
+ https://opensource.org/licenses/MIT
+ repo
+
+
+
+
+
+ b6k-dev
+ Maciej Bartczak
+ https://github.com/b6k-dev
+
+
+
- scm:git:git://github.com/b6k-dev/outcome.git
+ scm:git:https://github.com/b6k-dev/outcome.git
scm:git:ssh://git@github.com/b6k-dev/outcome.git
https://github.com/b6k-dev/outcome
+ HEAD
From 9bc5c995c351581ad0363f778c8df3ac1577af83 Mon Sep 17 00:00:00 2001
From: b6k <39600846+b6k-dev@users.noreply.github.com>
Date: Sat, 14 Mar 2026 11:32:56 +0100
Subject: [PATCH 3/8] pin java version to 17+
---
pom.xml | 37 ++++++++++++++++++-
src/test/java/b6k/dev/outcome/ResultTest.java | 32 ++++++++--------
2 files changed, 51 insertions(+), 18 deletions(-)
diff --git a/pom.xml b/pom.xml
index 7086353..6c810fc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -35,10 +35,12 @@
- 25
- 25
+ 17
+ 3.9.0
5.12.0
3.27.7
+ 3.14.1
+ 3.5.0
3.5.2
UTF-8
@@ -60,6 +62,37 @@
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${maven.compiler.release}
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ ${maven-enforcer-plugin.version}
+
+
+ enforce-build-environment
+
+ enforce
+
+
+
+
+ [17,)
+
+
+ [${maven.version},)
+
+
+
+
+
+
org.apache.maven.plugins
maven-surefire-plugin
diff --git a/src/test/java/b6k/dev/outcome/ResultTest.java b/src/test/java/b6k/dev/outcome/ResultTest.java
index c24377e..7387337 100644
--- a/src/test/java/b6k/dev/outcome/ResultTest.java
+++ b/src/test/java/b6k/dev/outcome/ResultTest.java
@@ -346,7 +346,7 @@ void panicsWhenSupplierThrows() {
@Test
void panicsWhenErrorMapperThrows() {
- assertThatThrownBy(() -> errResult().unwrapOrElse(_ -> {
+ assertThatThrownBy(() -> errResult().unwrapOrElse(error -> {
throw new IllegalStateException("boom");
}))
.isInstanceOf(Panic.class)
@@ -363,7 +363,7 @@ class OrElse {
void preservesValueForOk() {
var called = new AtomicBoolean(false);
- var result = okResult().orElse(_ -> {
+ var result = okResult().orElse(error -> {
called.set(true);
return Result.ok(23);
});
@@ -389,7 +389,7 @@ void recoversErrToOk() {
@Test
void transformsErrToDifferentErrorType() {
- Result result = errResult().orElse(_ -> Result.err(new IllegalStateException("new error")));
+ Result result = errResult().orElse(error -> Result.err(new IllegalStateException("new error")));
assertThat(result.unwrapError())
.isInstanceOf(IllegalStateException.class)
@@ -419,7 +419,7 @@ void rejectsNullFallback() {
@Test
void rejectsNullFallbackResult() {
- assertThatThrownBy(() -> errResult().orElse(_ -> null))
+ assertThatThrownBy(() -> errResult().orElse(error -> null))
.isInstanceOf(NullPointerException.class)
.hasMessage("fallback result must not be null");
}
@@ -470,7 +470,7 @@ void rejectsNullMapper() {
@Test
void panicsWhenMapperThrows() {
- assertThatThrownBy(() -> errResult().orThrow(_ -> {
+ assertThatThrownBy(() -> errResult().orThrow(error -> {
throw new IllegalStateException("boom");
}))
.isInstanceOf(Panic.class)
@@ -482,7 +482,7 @@ void panicsWhenMapperThrows() {
@Test
void rejectsNullMappedThrowable() {
- assertThatThrownBy(() -> errResult().orThrow(_ -> null))
+ assertThatThrownBy(() -> errResult().orThrow(error -> null))
.isInstanceOf(NullPointerException.class)
.hasMessage("errorMapper result must not be null");
}
@@ -526,7 +526,7 @@ void rejectsNullMapper() {
@Test
void rejectsNullMappedValue() {
- assertThatThrownBy(() -> okResult().map(_ -> null))
+ assertThatThrownBy(() -> okResult().map(value -> null))
.isInstanceOf(NullPointerException.class)
.hasMessage("value must not be null");
}
@@ -570,7 +570,7 @@ void rejectsNullMapper() {
@Test
void rejectsNullMappedError() {
- assertThatThrownBy(() -> errResult().mapError(_ -> null))
+ assertThatThrownBy(() -> errResult().mapError(error -> null))
.isInstanceOf(NullPointerException.class)
.hasMessage("error must not be null");
}
@@ -595,7 +595,7 @@ void preservesErrorForErr() {
@Test
void propagatesErrorFromMapper() {
- var result = okResult().flatMap(_ -> Result.err("mapper failed"));
+ var result = okResult().flatMap(value -> Result.err("mapper failed"));
assertTrue(result.isErr());
assertEquals("mapper failed", result.unwrapError());
@@ -633,7 +633,7 @@ void rejectsNullMapper() {
@Test
void rejectsNullMappedResult() {
- assertThatThrownBy(() -> okResult().flatMap(_ -> null))
+ assertThatThrownBy(() -> okResult().flatMap(value -> null))
.isInstanceOf(NullPointerException.class)
.hasMessage("mapper result must not be null");
}
@@ -658,7 +658,7 @@ void observesValueForOk() {
void preservesErrorForErrWithoutCallingObserver() {
var called = new AtomicBoolean(false);
- var result = errResult().peek(_ -> called.set(true));
+ var result = errResult().peek(value -> called.set(true));
assertEquals(errResult(), result);
assertFalse(called.get());
@@ -676,7 +676,7 @@ void rejectsNullObserver() {
@Test
void panicsWhenObserverThrows() {
- assertThatThrownBy(() -> okResult().peek(_ -> {
+ assertThatThrownBy(() -> okResult().peek(value -> {
throw new IllegalStateException("boom");
}))
.isInstanceOf(Panic.class)
@@ -706,7 +706,7 @@ void observesErrorForErr() {
void preservesValueForOkWithoutCallingObserver() {
var called = new AtomicBoolean(false);
- var result = okResult().peekErr(_ -> called.set(true));
+ var result = okResult().peekErr(error -> called.set(true));
assertEquals(okResult(), result);
assertFalse(called.get());
@@ -724,7 +724,7 @@ void rejectsNullObserver() {
@Test
void panicsWhenObserverThrows() {
- assertThatThrownBy(() -> errResult().peekErr(_ -> {
+ assertThatThrownBy(() -> errResult().peekErr(error -> {
throw new IllegalStateException("boom");
}))
.isInstanceOf(Panic.class)
@@ -841,7 +841,7 @@ void rejectsNullMappers() {
@Test
void panicsWhenOkMapperThrows() {
- assertThatThrownBy(() -> okResult().fold(_ -> {
+ assertThatThrownBy(() -> okResult().fold(value -> {
throw new IllegalStateException("boom");
}, String::length))
.isInstanceOf(Panic.class)
@@ -853,7 +853,7 @@ void panicsWhenOkMapperThrows() {
@Test
void panicsWhenErrorMapperThrows() {
- assertThatThrownBy(() -> errResult().fold(value -> value * 2, _ -> {
+ assertThatThrownBy(() -> errResult().fold(value -> value * 2, error -> {
throw new IllegalStateException("boom");
}))
.isInstanceOf(Panic.class)
From 78883554a49a5166b60b3e63d0dd33755666e573 Mon Sep 17 00:00:00 2001
From: b6k <39600846+b6k-dev@users.noreply.github.com>
Date: Sat, 14 Mar 2026 12:48:13 +0100
Subject: [PATCH 4/8] setup maven release plugins
---
.mvn/settings.xml | 9 ++++++
pom.xml | 74 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 83 insertions(+)
create mode 100644 .mvn/settings.xml
diff --git a/.mvn/settings.xml b/.mvn/settings.xml
new file mode 100644
index 0000000..60300f8
--- /dev/null
+++ b/.mvn/settings.xml
@@ -0,0 +1,9 @@
+
+
+
+ central
+ ${env.MAVEN_CENTRAL_USERNAME}
+ ${env.MAVEN_CENTRAL_PASSWORD}
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 6c810fc..f70721c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,7 +41,11 @@
3.27.7
3.14.1
3.5.0
+ 3.3.1
+ 3.11.2
+ 3.2.8
3.5.2
+ 0.9.0
UTF-8
@@ -101,4 +105,74 @@
+
+
+ release
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ ${maven-source-plugin.version}
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven-javadoc-plugin.version}
+
+ ${maven.compiler.release}
+ ${project.build.sourceEncoding}
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ ${maven-gpg-plugin.version}
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+ bc
+
+
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ ${central-publishing-maven-plugin.version}
+ true
+
+ central
+ ${project.artifactId}-${project.version}
+ false
+ false
+ validated
+
+
+
+
+
+
+
From 12bc34c094aca1d41fe8b19f296db447655cf57e Mon Sep 17 00:00:00 2001
From: b6k <39600846+b6k-dev@users.noreply.github.com>
Date: Sat, 14 Mar 2026 12:56:38 +0100
Subject: [PATCH 5/8] release workflow
---
.github/workflows/ci.yml | 2 +-
.github/workflows/release.yml | 95 +++++++++++++++++++++++++++++++++++
2 files changed, 96 insertions(+), 1 deletion(-)
create mode 100644 .github/workflows/release.yml
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a2abc37..1b475c7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,7 +16,7 @@ jobs:
uses: actions/setup-java@v5
with:
distribution: temurin
- java-version: '25'
+ java-version: '17'
cache: maven
- name: Make Maven wrapper executable
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..040825e
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,95 @@
+name: Release
+
+on:
+ workflow_dispatch:
+ inputs:
+ release_tag:
+ description: Release tag in MAJOR.MINOR.PATCH format
+ required: true
+ type: string
+ push:
+ tags:
+ - '*.*.*'
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ env:
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+ ref: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref }}
+
+ - name: Set up Java and Maven Central credentials
+ uses: actions/setup-java@v5
+ with:
+ distribution: temurin
+ java-version: '17'
+ cache: maven
+ server-id: central
+ server-username: ${{ secrets.SONATYPE_USERNAME }}
+ server-password: ${{ secrets.SONATYPE_PASSWORD }}
+ gpg-private-key: ${{ secrets.GPG_KEY_CONTENTS }}
+ gpg-passphrase: ${{ secrets.SIGNING_PASSWORD }}
+
+ - name: Make Maven wrapper executable
+ run: chmod +x ./mvnw
+
+ - name: Derive release and next snapshot versions from tag
+ env:
+ GITHUB_EVENT_NAME: ${{ github.event_name }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ RELEASE_TAG_INPUT: ${{ inputs.release_tag }}
+ run: |
+ if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then
+ RELEASE_VERSION="${RELEASE_TAG_INPUT}"
+ else
+ RELEASE_VERSION="${GITHUB_REF_NAME}"
+ fi
+
+ if ! [[ "${RELEASE_VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+ echo "Release tag '${RELEASE_VERSION}' is not in the expected MAJOR.MINOR.PATCH format." >&2
+ exit 1
+ fi
+
+ IFS='.' read -r major minor patch <> "${GITHUB_ENV}"
+ echo "NEXT_SNAPSHOT_VERSION=${NEXT_SNAPSHOT_VERSION}" >> "${GITHUB_ENV}"
+
+ - name: Set release version from tag
+ run: ./mvnw -B versions:set -DnewVersion="${RELEASE_VERSION}" -DgenerateBackupPoms=false
+
+ - name: Build, sign, and publish release bundle
+ run: ./mvnw -B -Prelease deploy
+
+ - name: Check out default branch for next snapshot bump
+ uses: actions/checkout@v6
+ with:
+ ref: ${{ env.DEFAULT_BRANCH }}
+ path: default-branch
+
+ - name: Bump default branch to next snapshot version
+ working-directory: /home/runner/work/outcome/outcome/default-branch
+ run: |
+ chmod +x ./mvnw
+ ./mvnw -B versions:set -DnewVersion="${NEXT_SNAPSHOT_VERSION}" -DgenerateBackupPoms=false
+ git config user.name "github-actions"
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
+ git add pom.xml
+ if git diff --cached --quiet; then
+ echo "${DEFAULT_BRANCH} is already at ${NEXT_SNAPSHOT_VERSION}; nothing to commit."
+ exit 0
+ fi
+ git commit -m "Bump version to ${NEXT_SNAPSHOT_VERSION}"
+ git push origin HEAD:"${DEFAULT_BRANCH}"
From 86efa2befe94200bb94b6675b003cbed85b78599 Mon Sep 17 00:00:00 2001
From: b6k <39600846+b6k-dev@users.noreply.github.com>
Date: Sat, 14 Mar 2026 14:24:39 +0100
Subject: [PATCH 6/8] delete settings.xml
---
.mvn/settings.xml | 9 ---------
1 file changed, 9 deletions(-)
delete mode 100644 .mvn/settings.xml
diff --git a/.mvn/settings.xml b/.mvn/settings.xml
deleted file mode 100644
index 60300f8..0000000
--- a/.mvn/settings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
- central
- ${env.MAVEN_CENTRAL_USERNAME}
- ${env.MAVEN_CENTRAL_PASSWORD}
-
-
-
\ No newline at end of file
From 95e5662e2a69d453a5cf2a5761f736cf8bb1c8c3 Mon Sep 17 00:00:00 2001
From: b6k <39600846+b6k-dev@users.noreply.github.com>
Date: Sat, 14 Mar 2026 14:24:55 +0100
Subject: [PATCH 7/8] readme improvements
---
README.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/README.md b/README.md
index f6d7415..58260b3 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,9 @@ Inspired by [better-result](https://github.com/dmmulroy/better-result).
## Contents
+- [Installation](#installation)
+- [Java Compatibility](#java-compatibility)
+- [Quick Start](#quick-start)
- [The Core Types](#the-core-types)
- [Creating Results](#creating-results)
- [Transforming Success Values](#transforming-success-values)
@@ -17,6 +20,65 @@ Inspired by [better-result](https://github.com/dmmulroy/better-result).
- [Panic](#panic)
- [API Reference](#api-reference)
+## Installation
+
+Maven:
+
+```xml
+
+ io.github.b6k-dev
+ outcome
+ 1.0.0
+
+```
+
+Gradle (Groovy DSL):
+
+```groovy
+implementation 'io.github.b6k-dev:outcome:1.0.0'
+```
+
+Gradle (Kotlin DSL):
+
+```kotlin
+implementation("io.github.b6k-dev:outcome:1.0.0")
+```
+
+## Java Compatibility
+
+Outcome targets Java 17 and newer. The library API is compiled with `--release 17`, and examples in this README use sealed types, records, and pattern matching features available in modern Java.
+
+## Quick Start
+
+```java
+import b6k.dev.outcome.Result;
+
+sealed interface CreateUserError permits InvalidEmail, DuplicateEmail {}
+record InvalidEmail(String value) implements CreateUserError {}
+record DuplicateEmail(String value) implements CreateUserError {}
+record User(String id, String email) {}
+
+Result createUser(String email) {
+ if (email == null || email.isBlank() || !email.contains("@")) {
+ return Result.err(new InvalidEmail(String.valueOf(email)));
+ }
+ if (email.equals("ada@example.com")) {
+ return Result.err(new DuplicateEmail(email));
+ }
+ return Result.ok(new User("u-123", email));
+}
+
+String message = createUser("ada@example.com").fold(
+ user -> "Created user " + user.email(),
+ error -> switch (error) {
+ case InvalidEmail e -> "Invalid email: " + e.value();
+ case DuplicateEmail e -> "Email already exists: " + e.email();
+ }
+);
+```
+
+Use `Result.ok(...)` and `Result.err(...)` to model expected outcomes, then compose them with methods like `map`, `flatMap`, `orElse`, and `fold` instead of mixing nullable values, sentinel states, and exceptions.
+
## The Core Types
Outcome revolves around four ideas:
From b82c6a8b49647e6e861f083d86cb019f42bea6da Mon Sep 17 00:00:00 2001
From: b6k <39600846+b6k-dev@users.noreply.github.com>
Date: Sat, 14 Mar 2026 14:40:23 +0100
Subject: [PATCH 8/8] finalize release workflow
---
.github/workflows/release.yml | 97 ++++++++++++++++++++++-------------
docs/releases/1.0.0.md | 23 +++++++++
2 files changed, 84 insertions(+), 36 deletions(-)
create mode 100644 docs/releases/1.0.0.md
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 040825e..fcdecfa 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -3,13 +3,14 @@ name: Release
on:
workflow_dispatch:
inputs:
- release_tag:
- description: Release tag in MAJOR.MINOR.PATCH format
+ release_version:
+ description: Release version in MAJOR.MINOR.PATCH format
required: true
type: string
- push:
- tags:
- - '*.*.*'
+
+concurrency:
+ group: release-${{ github.event.repository.default_branch }}
+ cancel-in-progress: false
jobs:
publish:
@@ -19,11 +20,11 @@ jobs:
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
steps:
- - name: Check out repository
+ - name: Check out default branch
uses: actions/checkout@v6
with:
fetch-depth: 0
- ref: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref }}
+ ref: ${{ github.event.repository.default_branch }}
- name: Set up Java and Maven Central credentials
uses: actions/setup-java@v5
@@ -40,20 +41,14 @@ jobs:
- name: Make Maven wrapper executable
run: chmod +x ./mvnw
- - name: Derive release and next snapshot versions from tag
+ - name: Derive release metadata
env:
- GITHUB_EVENT_NAME: ${{ github.event_name }}
- GITHUB_REF_NAME: ${{ github.ref_name }}
- RELEASE_TAG_INPUT: ${{ inputs.release_tag }}
+ RELEASE_VERSION_INPUT: ${{ inputs.release_version }}
run: |
- if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then
- RELEASE_VERSION="${RELEASE_TAG_INPUT}"
- else
- RELEASE_VERSION="${GITHUB_REF_NAME}"
- fi
+ RELEASE_VERSION="${RELEASE_VERSION_INPUT}"
if ! [[ "${RELEASE_VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
- echo "Release tag '${RELEASE_VERSION}' is not in the expected MAJOR.MINOR.PATCH format." >&2
+ echo "Release version '${RELEASE_VERSION}' is not in the expected MAJOR.MINOR.PATCH format." >&2
exit 1
fi
@@ -63,33 +58,63 @@ jobs:
NEXT_PATCH=$((patch + 1))
NEXT_SNAPSHOT_VERSION="${major}.${minor}.${NEXT_PATCH}-SNAPSHOT"
+ RELEASE_NOTES_FILE="docs/releases/${RELEASE_VERSION}.md"
echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "${GITHUB_ENV}"
echo "NEXT_SNAPSHOT_VERSION=${NEXT_SNAPSHOT_VERSION}" >> "${GITHUB_ENV}"
+ echo "RELEASE_NOTES_FILE=${RELEASE_NOTES_FILE}" >> "${GITHUB_ENV}"
- - name: Set release version from tag
- run: ./mvnw -B versions:set -DnewVersion="${RELEASE_VERSION}" -DgenerateBackupPoms=false
+ - name: Verify release tag does not already exist
+ run: |
+ if git rev-parse -q --verify "refs/tags/${RELEASE_VERSION}" >/dev/null; then
+ echo "Tag ${RELEASE_VERSION} already exists locally." >&2
+ exit 1
+ fi
+ if git ls-remote --exit-code --tags origin "refs/tags/${RELEASE_VERSION}" >/dev/null; then
+ echo "Tag ${RELEASE_VERSION} already exists on origin." >&2
+ exit 1
+ fi
- - name: Build, sign, and publish release bundle
- run: ./mvnw -B -Prelease deploy
+ - name: Configure git author
+ run: |
+ git config user.name "github-actions"
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- - name: Check out default branch for next snapshot bump
- uses: actions/checkout@v6
- with:
- ref: ${{ env.DEFAULT_BRANCH }}
- path: default-branch
+ - name: Create release commit and tag
+ run: |
+ ./mvnw -B versions:set -DnewVersion="${RELEASE_VERSION}" -DgenerateBackupPoms=false
+ git add pom.xml
+ git commit -m "Release ${RELEASE_VERSION}"
+ git tag -a "${RELEASE_VERSION}" -m "Release ${RELEASE_VERSION}"
- - name: Bump default branch to next snapshot version
- working-directory: /home/runner/work/outcome/outcome/default-branch
+ - name: Create next snapshot commit
run: |
- chmod +x ./mvnw
./mvnw -B versions:set -DnewVersion="${NEXT_SNAPSHOT_VERSION}" -DgenerateBackupPoms=false
- git config user.name "github-actions"
- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add pom.xml
- if git diff --cached --quiet; then
- echo "${DEFAULT_BRANCH} is already at ${NEXT_SNAPSHOT_VERSION}; nothing to commit."
- exit 0
- fi
git commit -m "Bump version to ${NEXT_SNAPSHOT_VERSION}"
- git push origin HEAD:"${DEFAULT_BRANCH}"
+
+ - name: Push release tag and default branch
+ run: |
+ git push --atomic origin HEAD:"${DEFAULT_BRANCH}" "refs/tags/${RELEASE_VERSION}"
+
+ - name: Check out release tag for publishing
+ run: git checkout --detach "${RELEASE_VERSION}"
+
+ - name: Build, sign, and publish release bundle
+ run: ./mvnw -B -Prelease deploy
+
+ - name: Create draft GitHub Release
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ if [ -f "${RELEASE_NOTES_FILE}" ]; then
+ gh release create "${RELEASE_VERSION}" \
+ --draft \
+ --title "${RELEASE_VERSION}" \
+ --notes-file "${RELEASE_NOTES_FILE}"
+ else
+ gh release create "${RELEASE_VERSION}" \
+ --draft \
+ --title "${RELEASE_VERSION}" \
+ --generate-notes
+ fi
diff --git a/docs/releases/1.0.0.md b/docs/releases/1.0.0.md
new file mode 100644
index 0000000..25864eb
--- /dev/null
+++ b/docs/releases/1.0.0.md
@@ -0,0 +1,23 @@
+# Outcome 1.0.0 Release Notes
+
+## Highlights
+
+- First public release of Outcome, a lightweight `Result` type for Java
+
+## Coordinates
+
+Maven:
+
+```xml
+
+ io.github.b6k-dev
+ outcome
+ 1.0.0
+
+```
+
+Gradle:
+
+```groovy
+implementation 'io.github.b6k-dev:outcome:1.0.0'
+```
\ No newline at end of file