diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
new file mode 100644
index 0000000..2235e21
--- /dev/null
+++ b/.github/workflows/release.yaml
@@ -0,0 +1,76 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+name: Release Project
+
+# This workflow is triggered manually from the Actions tab.
+on:
+ workflow_dispatch:
+ inputs:
+ version:
+ description: 'The version to release'
+ required: true
+ type: string
+ sonatype_auto_publish:
+ description: 'Automatically publish the release to Sonatype'
+ type: boolean
+ default: false
+ required: true
+ create_tag:
+ description: 'Create a git tag'
+ type: boolean
+ default: true
+ required: true
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+
+ permissions:
+ # required for creating git tags.
+ contents: write
+ # required by the bazel-contrib/publish-to-bcr action
+ # pull-requests: write
+
+ steps:
+ - name: Checkout current commit
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+
+ # Maven and Bazel require Java to be available.
+ - name: Setup Java
+ uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
+ with:
+ java-version: '21'
+ distribution: 'zulu'
+ java-package: jdk
+ # This will create the maven settings.xml on the fly
+ server-id: central
+ server-username: ${{ secrets.MAVEN_USERNAME }}
+ server-password: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
+ gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
+ gpg-passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
+
+ - name: Setup Bazel
+ uses: bazelbuild/setup-bazelisk@v3
+
+ - name: Publish to Sonatype
+ run: |
+ ./maven/release_jsinterop_base.sh --version ${{ github.event.inputs.version }} --sonatype-auto-publish ${{ github.event.inputs.sonatype_auto_publish }}
+
+ - name: Create Git Tag
+ if: github.event.inputs.create_tag == 'true'
+ run: |
+ git config --local user.email "action@github.com"
+ git config --local user.name "GitHub Action"
+ git tag -a "${{ github.event.inputs.version }}" -m "Release ${{ github.event.inputs.version }}"
+ git push origin "${{ github.event.inputs.version }}"
diff --git a/maven/deploy.sh b/maven/deploy.sh
index b1616b8..3c021c8 100644
--- a/maven/deploy.sh
+++ b/maven/deploy.sh
@@ -265,10 +265,3 @@ common::cleanup_temp_files() {
rm -rf "${maven_wd}"
fi
}
-
-common::create_and_push_git_tag() {
- local lib_version="$1"
- common::info "Creating git tag: ${lib_version}"
- git tag -a "${lib_version}" -m "${lib_version} release"
- git push origin "${lib_version}"
-}
diff --git a/maven/plugins.xml b/maven/plugins.xml
new file mode 100644
index 0000000..81c8747
--- /dev/null
+++ b/maven/plugins.xml
@@ -0,0 +1,70 @@
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.4.2
+
+ ${classesDirectory}
+
+
+
+ org.codehaus.plexus
+ plexus-io
+
+ 3.5.1
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 1.9.1
+
+
+ attach-artifacts
+ package
+
+ attach-artifact
+
+
+
+
+ ${javadocFile}
+ jar
+ javadoc
+
+
+ ${sourcesFile}
+ jar
+ sources
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 3.2.7
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.7.0
+ true
+
+ ossrh
+ ${nexusUrl}
+
+
+
diff --git a/maven/publish_to_sonatype.sh b/maven/publish_to_sonatype.sh
new file mode 100755
index 0000000..298b358
--- /dev/null
+++ b/maven/publish_to_sonatype.sh
@@ -0,0 +1,86 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The script generates the open source artifacts for jsinterop.base and
+# uploads them to sonatype.
+set -euo pipefail
+
+source "$(dirname "$0")/deploy.sh"
+
+readonly MAVEN_ARTIFACT="base"
+readonly BAZEL_ARTIFACT="base"
+readonly BAZEL_PATH="java/jsinterop/base"
+
+
+usage() {
+ echo ""
+ echo "$(basename $0): Build and deploy script for JsInterop Base."
+ echo ""
+ echo "$(basename $0) --version [--no-deploy]"
+ echo " --help"
+ echo " Print this help output and exit."
+ echo " --version "
+ echo " Maven version of the library to use for deploying to sonatype."
+ echo " --no-deploy"
+ echo " Skip the deployment part but build all artifacts."
+ echo " --sonatype-auto-publish"
+ echo " Publish the artifact on sonatype automatically after upload."
+ echo ""
+}
+
+parse_arguments() {
+ deploy_to_sonatype=true
+ lib_version=""
+ sonatype_auto_publish=false
+
+ while [[ $# -gt 0 ]]; do
+ case $1 in
+ --version )
+ shift
+ lib_version=$1
+ ;;
+ --no-deploy )
+ deploy_to_sonatype=false
+ ;;
+ --sonatype-auto-publish)
+ sonatype_auto_publish=true
+ shift
+ ;;
+ --help )
+ usage
+ exit 0
+ ;;
+ * )
+ common::error "unexpected option $1"
+ ;;
+ esac
+ shift
+ done
+}
+
+main() {
+ parse_arguments "$@"
+
+ common::check_version_set
+ common::check_bazel_prerequisites
+ common::check_maven_prerequisites
+
+ common::build
+ common::deploy_to_sonatype
+}
+
+# Set the trap to cleanup temporary files on EXIT
+trap common::cleanup_temp_files EXIT
+
+main "$@"
diff --git a/maven/utils.sh b/maven/utils.sh
new file mode 100644
index 0000000..da07fca
--- /dev/null
+++ b/maven/utils.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+# Copyright 2019 Google Inc. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script provides common function and variables used by other scripts to
+# release specific projects.
+readonly BAZEL_ROOT=$(pwd)
+
+j2cl_utils::error() {
+ echo "Error: $1"
+ exit 1
+}
+
+j2cl_utils::info() {
+ echo "Info: $1"
+}
+
+
+#######################################
+# Check if Bazel is installed and the script is run from the root of the Bazel
+# repository.
+# Globals:
+# BAZEL
+#######################################
+j2cl_utils::check_bazel() {
+ if [ ! -f "MODULE.bazel" ]; then
+ error "This script must be run from the root of the Bazel repository (where MODULE.bazel exists)."
+ fi
+
+ # Check for Bazel or Bazelisk
+ if command -v bazelisk &> /dev/null; then
+ BAZEL="bazelisk"
+ elif command -v bazel &> /dev/null; then
+ BAZEL="bazel"
+ else
+ error "Neither Bazel nor Bazelisk is installed or in your PATH."
+ fi
+}
+
+#######################################
+# Check if Maven is installed.
+#######################################
+j2cl_utils::check_maven() {
+ if ! command -v mvn &> /dev/null; then
+ error "Maven is not installed. Please install it."
+ fi
+
+ # TODO(dramaix): Do we still need this?
+ if ! command -v gpg2 &> /dev/null; then
+ error "gpg2 is not installed. Please install it."
+ fi
+}
+
+bazel_build() {
+ info "Building Bazel target: ${1}"
+ "${BAZEL}" build "${1}"
+}
+
+