diff --git a/.github/workflows/test-version-bump.yml b/.github/workflows/test-version-bump.yml
new file mode 100644
index 00000000..6c4a4689
--- /dev/null
+++ b/.github/workflows/test-version-bump.yml
@@ -0,0 +1,53 @@
+---
+
+name: Test Version Bump Action
+
+on:
+ workflow_dispatch:
+ inputs:
+ version_number:
+ description: "New Version"
+ required: true
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Bump JSON Test
+ id: test_json
+ uses: ./version-bump
+ with:
+ version: ${{ github.event.inputs.version_number }}
+ file_path: "./version-bump/tests/package-lock.json"
+
+ - name: Bump PLIST Test
+ id: test_plist
+ uses: ./version-bump
+ with:
+ version: ${{ github.event.inputs.version_number }}
+ file_path: "./version-bump/tests/Info.plist"
+
+ - name: Bump XML Test
+ id: test_xml
+ uses: ./version-bump
+ with:
+ version: ${{ github.event.inputs.version_number }}
+ file_path: "./version-bump/tests/AndroidManifest.xml"
+
+ - name: Bump Props Test
+ id: test_props
+ uses: ./version-bump
+ with:
+ version: ${{ github.event.inputs.version_number }}
+ file_path: "./version-bump/tests/dir.build.props"
+
+ - name: Validate Outputs
+ run: |
+ echo "${{ steps.test_json.outputs.status }}"
+ echo "${{ steps.test_plist.outputs.status }}"
+ echo "${{ steps.test_xml.outputs.status }}"
+ echo "${{ steps.test_props.outputs.status }}"
+
+
\ No newline at end of file
diff --git a/version-bump/Dockerfile b/version-bump/Dockerfile
new file mode 100644
index 00000000..cc3945f8
--- /dev/null
+++ b/version-bump/Dockerfile
@@ -0,0 +1,11 @@
+FROM python:3-slim
+
+ADD . /app
+
+WORKDIR /app
+
+RUN pip3 install lxml --target=/app
+
+ENV PYTHONPATH /app
+
+ENTRYPOINT [ "python", "/app/main.py" ]
\ No newline at end of file
diff --git a/version-bump/README.md b/version-bump/README.md
new file mode 100644
index 00000000..c2c213b2
--- /dev/null
+++ b/version-bump/README.md
@@ -0,0 +1,14 @@
+# Version Bump Action
+
+A Github Action that will replace versions in JSON, PLIST, and XML files. Specifically created for interacting with AndroidManifest, iOS development plists, and Node package JSON files.
+
+## Usage
+
+```yml
+- name: Bump Android Version
+uses: ./version-bump
+with:
+ version: ${{ github.event.inputs.version_number }}
+ file_path: "./AndroidManifest.xml"
+```
+
diff --git a/version-bump/action.yml b/version-bump/action.yml
new file mode 100644
index 00000000..6f8ec38f
--- /dev/null
+++ b/version-bump/action.yml
@@ -0,0 +1,21 @@
+name: "PLIST/XML/JSON Version Bump"
+description: "Replaces versions in PLIST/JSON/XML for easier version updating."
+author: "Bitwarden"
+branding:
+ icon: download
+ color: blue
+inputs:
+ version:
+ description: "Newest version to use."
+ default: "1.0"
+ required: true
+ file_path:
+ description: "Path to the file to apply the new version."
+ default: "./"
+ required: true
+outputs:
+ status:
+ description: "Status"
+runs:
+ using: "docker"
+ image: "Dockerfile"
\ No newline at end of file
diff --git a/version-bump/main.py b/version-bump/main.py
new file mode 100644
index 00000000..eda56dbf
--- /dev/null
+++ b/version-bump/main.py
@@ -0,0 +1,81 @@
+import os
+import json
+import plistlib
+import lxml.etree as ET
+
+
+def get_file_type(file):
+ file_type = os.path.splitext(file)[1]
+ return file_type
+
+
+def update_json(version, file):
+ with open(file) as json_file:
+ data = json.load(json_file)
+ data["version"] = version
+ try:
+ data["packages"][""]["version"] = version
+ except KeyError:
+ pass
+ json.dump(data, open(file, "w"), indent=2)
+
+
+def update_plist(version, file):
+ with open(file, "rb") as plist:
+
+ pl = plistlib.load(plist)
+ pl["CFBundleShortVersionString"] = version
+
+ data = pl
+ with open(file, "wb") as update_plist:
+ plistlib.dump(data, update_plist, sort_keys=False)
+
+
+def update_xml(version, file):
+ mytree = ET.parse(file)
+ myroot = mytree.getroot()
+
+ # Android Manifests
+ if myroot.tag == "manifest":
+ myroot.attrib[
+ "{http://schemas.android.com/apk/res/android}versionName"
+ ] = version
+ ET.register_namespace(
+ "android", "http://schemas.android.com/apk/res/android")
+ ET.register_namespace(
+ "tools", "http://schemas.android.com/tools")
+ mytree.write(
+ file, encoding="utf-8", xml_declaration=True, pretty_print=True)
+ else:
+ # MSBuild Props
+ myroot[0][1].text = version
+ mytree.write(
+ file, encoding="utf-8"
+ )
+
+
+if __name__ == "__main__":
+ version = os.getenv("INPUT_VERSION")
+ file_path = os.getenv("INPUT_FILE_PATH")
+
+ # Throw an exception if there is no file path defined.
+ try:
+ os.path.isfile(file_path)
+ except TypeError:
+ raise Exception(f"File path for {file_path} not found.")
+
+
+ file_type = get_file_type(file_path)
+
+
+ # Handle the file based on the extension.
+ if file_type == ".xml" or file_type == ".props":
+ update_xml(version, file_path)
+ elif file_type == ".json":
+ update_json(version, file_path)
+ elif file_type == ".plist":
+ update_plist(version, file_path)
+ else:
+ raise Exception("No file was recognized as a supported format.")
+
+ print(f"::set-output name=status::Updated {file_path}")
diff --git a/version-bump/tests/AndroidManifest.xml b/version-bump/tests/AndroidManifest.xml
new file mode 100644
index 00000000..06a89503
--- /dev/null
+++ b/version-bump/tests/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/version-bump/tests/Info.plist b/version-bump/tests/Info.plist
new file mode 100644
index 00000000..9f0bde6a
--- /dev/null
+++ b/version-bump/tests/Info.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ MinimumOSVersion
+ 10.0
+ CFBundleDisplayName
+ Acme
+ CFBundleName
+ Acme Extension
+ CFBundleIdentifier
+ com.org.Acme.ext
+ CFBundleShortVersionString
+ 6.20
+ CFBundleLocalizations
+ CFBundleDevelopmentRegion
+ en
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundlePackageType
+ XPC!
+ CFBundleSignature
+ ????
+ UIDeviceFamily
+
+
diff --git a/version-bump/tests/dif.build.props b/version-bump/tests/dif.build.props
new file mode 100644
index 00000000..2a5ccbfc
--- /dev/null
+++ b/version-bump/tests/dif.build.props
@@ -0,0 +1,9 @@
+
+
+
+ net5.0
+ 5.8
+ Acme.$(MSBuildProjectName)
+
+
+
\ No newline at end of file
diff --git a/version-bump/tests/package-lock.json b/version-bump/tests/package-lock.json
new file mode 100644
index 00000000..26b32811
--- /dev/null
+++ b/version-bump/tests/package-lock.json
@@ -0,0 +1,11 @@
+{
+ "name": "acme-test",
+ "version": "1.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "version": "1.0",
+ }
+ }
+}
\ No newline at end of file