Browse Source

DEVOPS-1840 - Add Version Bump Calculation (#256)

pull/257/head
Vince Grassia 2 years ago committed by GitHub
parent
commit
f79ea6135e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 21
      .github/workflows/test-version-bump.yml
  2. 3
      version-bump/Dockerfile
  3. 9
      version-bump/action.yml
  4. 67
      version-bump/main.py
  5. 2
      version-bump/tests/fixtures/AndroidManifest.xml
  6. 2
      version-bump/tests/fixtures/Info.plist
  7. 2
      version-bump/tests/fixtures/dir.build.props
  8. 4
      version-bump/tests/fixtures/package-lock.json
  9. 2
      version-bump/tests/fixtures/test.csproj

21
.github/workflows/test-version-bump.yml

@ -7,7 +7,7 @@ on: @@ -7,7 +7,7 @@ on:
inputs:
version_number:
description: "New Version"
required: true
required: false
jobs:
test-version-bumps:
@ -19,41 +19,50 @@ jobs: @@ -19,41 +19,50 @@ jobs:
id: test_json
uses: ./version-bump
with:
version: ${{ github.event.inputs.version_number }}
version: ${{ inputs.version_number }}
file_path: "./version-bump/tests/fixtures/package-lock.json"
- name: Bump PLIST Test
id: test_plist
uses: ./version-bump
with:
version: ${{ github.event.inputs.version_number }}
version: ${{ inputs.version_number }}
file_path: "./version-bump/tests/fixtures/Info.plist"
- name: Bump XML Test
id: test_xml
uses: ./version-bump
with:
version: ${{ github.event.inputs.version_number }}
version: ${{ inputs.version_number }}
file_path: "./version-bump/tests/fixtures/AndroidManifest.xml"
- name: Bump Props Test
id: test_props
uses: ./version-bump
with:
version: ${{ github.event.inputs.version_number }}
version: ${{ inputs.version_number }}
file_path: "./version-bump/tests/fixtures/dir.build.props"
- name: Bump CSProj Test
id: test_csproj
uses: ./version-bump
with:
version: ${{ github.event.inputs.version_number }}
version: ${{ inputs.version_number }}
file_path: "./version-bump/tests/fixtures/test.csproj"
- name: Validate Outputs
run: |
echo "${{ steps.test_json.outputs.status }}"
echo "${{ steps.test_json.outputs.version }}"
echo "${{ steps.test_plist.outputs.status }}"
echo "${{ steps.test_plist.outputs.version }}"
echo "${{ steps.test_xml.outputs.status }}"
echo "${{ steps.test_xml.outputs.version }}"
echo "${{ steps.test_props.outputs.status }}"
echo "${{ steps.test_props.outputs.version }}"
echo "${{ steps.test_csproj.outputs.status }}"
echo "${{ steps.test_csproj.outputs.version }}"

3
version-bump/Dockerfile

@ -9,4 +9,5 @@ RUN pip3 install pyyaml --target=/app @@ -9,4 +9,5 @@ RUN pip3 install pyyaml --target=/app
ENV PYTHONPATH /app
ENTRYPOINT [ "python", "/app/main.py" ]
CMD ["/app/main.py"]
ENTRYPOINT [ "python", "-u" ]

9
version-bump/action.yml

@ -6,16 +6,17 @@ branding: @@ -6,16 +6,17 @@ branding:
color: blue
inputs:
version:
description: "Newest version to use."
default: "1.0"
required: true
description: "New version to use."
default: "2023.12.1"
required: false
file_path:
description: "Path to the file to apply the new version."
default: "./"
required: true
outputs:
status:
description: "Status"
version:
description: "New Version"
runs:
using: "docker"
image: "Dockerfile"

67
version-bump/main.py

@ -1,11 +1,24 @@ @@ -1,11 +1,24 @@
import os
from datetime import date
import json
import lxml.etree as ET
import os
import plistlib
import re
import lxml.etree as ET
import yaml
def get_next_version(version):
version_split = version.split('.')
if len(version_split) < 3:
raise Exception("Version does not have year, month, and patch.")
year = int(version_split[0])
month = int(version_split[1])
patch = int(version_split[2])
current_date = date(date.today().year, date.today().month, 1)
patch = 0 if year != current_date.year or month != current_date.month else patch + 1
return f"{current_date.year}.{current_date.month}.{patch}"
def get_file_type(file_path):
file_type = os.path.splitext(file_path)[1]
return file_type
@ -16,74 +29,91 @@ def get_file_name(file_path): @@ -16,74 +29,91 @@ def get_file_name(file_path):
return file_name
def update_json(version, file_path):
def update_json(file_path, version=None):
with open(file_path) as json_file:
data = json.load(json_file)
data["version"] = version
data["version"] = version if version is not None else get_next_version(data["version"])
try:
data["packages"][""]["version"] = version
data["packages"][""]["version"] = version if version is not None else get_next_version(data["packages"][""]["version"])
except KeyError:
pass
json.dump(data, open(file_path, "w"), indent=2)
with open(file_path, "a") as f:
f.write("\n") # Make sure we add the new line back in at EOF.
return data["version"]
def update_plist(version, file_path):
def update_plist(file_path, version=None):
with open(file_path, "rb") as plist:
pl = plistlib.load(plist)
pl["CFBundleShortVersionString"] = version
pl["CFBundleShortVersionString"] = version if version is not None else get_next_version(pl["CFBundleShortVersionString"])
data = pl
with open(file_path, "wb") as update_plist:
plistlib.dump(data, update_plist, sort_keys=False)
return pl["CFBundleShortVersionString"]
def update_xml(version, file_path):
def update_xml(file_path, version=None):
mytree = ET.parse(file_path)
myroot = mytree.getroot()
# Android Manifests
if myroot.tag == "manifest":
new_version = version if version is not None else get_next_version(myroot.attrib.get('{http://schemas.android.com/apk/res/android}versionName'))
with open(file_path, "r") as f:
data = f.read()
data_new = re.sub(
'android:versionName="[0-9]+\.[0-9]+\.[0-9]+"',
f'android:versionName="{version}"',
r'android:versionName="[0-9]+\.[0-9]+\.[0-9]+"',
f'android:versionName="{new_version}"',
data,
flags=re.M,
)
with open(file_path, "w") as f:
f.write(data_new)
return new_version
# Microsoft .NET project files
elif myroot.attrib.has_key("Sdk") and "Microsoft.NET.Sdk" in myroot.attrib["Sdk"]:
version_property = [x for x in myroot[0] if x.tag == "Version"][-1]
version_property.text = version
version_property.text = version if version is not None else get_next_version(version_property.text)
mytree.write(file_path)
return version_property.text
# MSBuild Props
else:
version_property = [x for x in myroot[0] if x.tag == "Version"][-1]
version_property.text = version
version_property.text = version if version is not None else get_next_version(version_property.text)
mytree.write(file_path, encoding="utf-8")
return version_property.text
# For updating Helm Charts - Chart.yaml version
def update_yaml(version, file_path):
def update_yaml(file_path, version=None):
with open(file_path, "r") as f:
doc = yaml.load(f, Loader=yaml.FullLoader)
doc["version"] = version
doc["version"] = version if version is not None else get_next_version(doc["version"])
with open(file_path, "w") as f:
yaml.dump(doc, f)
return doc["version"]
if __name__ == "__main__":
version = os.getenv("INPUT_VERSION")
file_path = os.getenv("INPUT_FILE_PATH")
# This fixes GitHub passing in an empty string instead of not setting the environment variable.
if version == "":
version = None
# Throw an exception if there is no file path defined.
try:
os.path.isfile(file_path)
@ -95,17 +125,18 @@ if __name__ == "__main__": @@ -95,17 +125,18 @@ if __name__ == "__main__":
# Handle the file based on the extension.
if file_type in {".xml", ".props", ".csproj"}:
update_xml(version, file_path)
new_version = update_xml(file_path, version)
elif file_type == ".json":
update_json(version, file_path)
new_version = update_json(file_path, version)
elif file_type == ".plist":
update_plist(version, file_path)
new_version = update_plist(file_path, version)
elif file_name == "Chart.yaml" or file_name == "Chart.yml":
update_yaml(version, file_path)
new_version = update_yaml(file_path, version)
else:
raise Exception("No file was recognized as a supported format.")
if "GITHUB_OUTPUT" in os.environ:
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
print("{0}={1}".format("status", f"Updated {file_path}"), file=f)
print("{0}={1}".format("version", f"New Version: {new_version}"), file=f)

2
version-bump/tests/fixtures/AndroidManifest.xml vendored

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="10.2" android:installLocation="internalOnly" package="com.acme.test">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2023.12.1" android:installLocation="internalOnly" package="com.acme.test">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.NFC"/>

2
version-bump/tests/fixtures/Info.plist vendored

@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
<key>CFBundleIdentifier</key>
<string>com.org.Acme.ext</string>
<key>CFBundleShortVersionString</key>
<string>6.20</string>
<string>2023.12.1</string>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>

2
version-bump/tests/fixtures/dir.build.props vendored

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<Version>5.8</Version>
<Version>2023.12.1</Version>
<RootNamespace>Acme.$(MSBuildProjectName)</RootNamespace>
</PropertyGroup>

4
version-bump/tests/fixtures/package-lock.json generated vendored

@ -1,11 +1,11 @@ @@ -1,11 +1,11 @@
{
"name": "acme-test",
"version": "1.0",
"version": "2023.12.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "1.0"
"version": "2023.12.1"
}
}
}

2
version-bump/tests/fixtures/test.csproj vendored

@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
<RootNamespace>Bit.KeyConnector</RootNamespace>
<UserSecretsId>bitwarden-KeyConnector</UserSecretsId>
<GenerateRuntimeConfigurationFiles>True</GenerateRuntimeConfigurationFiles>
<Version>1.0.0</Version>
<Version>2023.12.1</Version>
</PropertyGroup>
<ItemGroup>

Loading…
Cancel
Save