From 6b242c45cee3c1182840804bcdbcc35dfeb51119 Mon Sep 17 00:00:00 2001 From: Carlos Lapao Date: Fri, 1 Nov 2024 08:24:31 +0000 Subject: [PATCH] further automation for the project --- .github/ISSUE_TEMPLATE/bug-report.yml | 83 ++++++ .github/ISSUE_TEMPLATE/feature-request.yml | 35 +++ .../ISSUE_TEMPLATE/product-backlog-item.yml | 69 +++++ .github/pull_request_template.md | 7 +- .github/workflow_scripts/current-version.sh | 20 ++ .../workflow_scripts/generate-blog-records.sh | 54 ++++ .../workflow_scripts/generate-changelog.sh | 242 ++++++++++++++++++ .../generate-release-notes.sh | 34 +++ .../get-latest-changelog-version.sh | 6 + .../workflow_scripts/get-latest-changelog.sh | 91 +++++++ .github/workflow_scripts/increment-version.sh | 43 ++++ CHANGELOG.md | 23 +- VERSION | 1 + 13 files changed, 696 insertions(+), 12 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature-request.yml create mode 100644 .github/ISSUE_TEMPLATE/product-backlog-item.yml create mode 100755 .github/workflow_scripts/current-version.sh create mode 100755 .github/workflow_scripts/generate-blog-records.sh create mode 100755 .github/workflow_scripts/generate-changelog.sh create mode 100755 .github/workflow_scripts/generate-release-notes.sh create mode 100755 .github/workflow_scripts/get-latest-changelog-version.sh create mode 100755 .github/workflow_scripts/get-latest-changelog.sh create mode 100755 .github/workflow_scripts/increment-version.sh create mode 100644 VERSION diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000..86690c9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,83 @@ +--- +name: Bug report +description: Create a bug report +title: "[BUG] " +labels: + - bug + - triage +assignees: + - cjlapao +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Search to see if an issue already exists for the bug you encountered. + options: + - label: I have searched the existing issues + required: true + - type: textarea + attributes: + label: Current Behavior + description: A concise description of what you're experiencing. + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: A concise description of what you expected to happen. + validations: + required: true + - type: textarea + attributes: + label: DevOps Service version + description: | + DevOps Service version where you observed this issue + placeholder: | + vX.Y.Z + render: Markdown + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: | + Copy and paste any relevant log output. + This will be automatically formatted into code, so no need for backticks. + Enable debug logging, when running it by setting the environment variable LOG_LEVEL. + render: Shell + validations: + required: true + - type: textarea + attributes: + label: Steps To Reproduce + description: | + Steps to reproduce the issue. + To speed up the triaging of your request, reproduce the issue running + placeholder: | + 1. In this environment... + 1. With this config... + 1. Run '...' + 1. See error... + validations: + required: true + - type: textarea + attributes: + label: Anything else? + description: | + Links? References? Anything that will give us more context about the issue you are encountering! + + Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. + validations: + required: false + - type: checkboxes + id: validation + attributes: + label: Validation + options: + - label: Yes, I've included all of the above information (Version, settings, logs, etc.) + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 0000000..1e0760a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,35 @@ +name: Feature Request +description: Suggest an idea for this project +title: "[FEATURE]" +labels: + - triage + - feature request +assignees: + - cjlapao +body: + - type: textarea + id: feature_description + attributes: + label: Is your feature request related to a problem? Please describe. + value: |- + A clear and concise description of what the problem is. Ex. I'm always frustrated + when [...] + validations: + required: true + - type: textarea + id: describe_feature + attributes: + label: Describe how would you expect the feature to work + value: A clear and concise description of what you want to happen. + validations: + required: true + - type: textarea + id: describe_alternatives + attributes: + label: Describe alternatives you've considered + value: A clear and concise description of any alternative solutions or features you've considered. + - type: textarea + id: additional_context + attributes: + label: Additional context + value: Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/product-backlog-item.yml b/.github/ISSUE_TEMPLATE/product-backlog-item.yml new file mode 100644 index 0000000..ddbd79d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/product-backlog-item.yml @@ -0,0 +1,69 @@ +name: Product Backlog Item +description: Create a product backlog item +title: "[PBI] " +labels: + - triage + - pbi +assignees: + - cjlapao +projects: + - "Parallels/6" +body: + - type: textarea + id: description + attributes: + label: Description + placeholder: |- + Provide a clear and concise description of the item. Explain the purpose and the expected outcome. + validations: + required: true + - type: textarea + id: user_story + attributes: + label: User Story + placeholder: |- + As a **[type of user]**, I want **[some goal]** so that **[some reason]**. + validations: + required: true + - type: textarea + id: describe_feature + attributes: + label: Acceptance Criteria + placeholder: |- + - [ ] **Criterion 1**: Detailed description of the first acceptance criterion. + description: | + Define the conditions that must be met for the item to be considered complete. + validations: + required: true + - type: checkboxes + id: definition_of_done + attributes: + label: Definition of Done + options: + - label: Code implemented following best practices. + - label: Unit tests written and passing. + - label: Code reviewed and approved. + - label: Merged into the main branch. + - label: Documentation updated (if applicable). + - label: Deployed to staging/production environment. + + - type: textarea + id: assumptions_and_constraints + attributes: + label: Assumptions and Constraints + placeholder: |- + - **Assumption 1**: Detailed description of the first assumption. + - List any assumptions that are being made about the item. + - **Constraint 1**: Detailed description of the first constraint. + - List any constraints that may impact the implementation of the item. + - type: textarea + id: dependencies + attributes: + label: Assumptions and Constraints + placeholder: |- + - **Dependency 1**: Detailed description of the first dependency. + - type: textarea + id: additional_context + attributes: + label: Additional Notes + placeholder: Add any other context or screenshots about the item here. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 53116db..a212eb3 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,7 +2,7 @@ Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. -Fixes # (issue) +fixes # (issue) ## Type of change @@ -13,12 +13,13 @@ Please delete options that are not relevant. - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] This change requires a documentation update +- [ ] Updated packages -### Checklist: +### Checklist - [ ] I have performed a self-review of my own code - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have run linting on my code - [ ] I have run tests that prove my fix is effective or that my feature works -- [ ] I have updated the CHANGELOG.md file accordingly \ No newline at end of file +- [ ] I have updated the CHANGELOG.md file accordingly diff --git a/.github/workflow_scripts/current-version.sh b/.github/workflow_scripts/current-version.sh new file mode 100755 index 0000000..23dde02 --- /dev/null +++ b/.github/workflow_scripts/current-version.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +while getopts ":f:" opt; do + case $opt in + f) + FILE=$OPTARG + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + esac +done + +if [ -z "$FILE" ]; then + echo "You need to specify the version file with the -f flag" +fi + +VERSION=$(cat "$FILE") +echo "$VERSION" diff --git a/.github/workflow_scripts/generate-blog-records.sh b/.github/workflow_scripts/generate-blog-records.sh new file mode 100755 index 0000000..9327df7 --- /dev/null +++ b/.github/workflow_scripts/generate-blog-records.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# Initialize variables +version="" +date="" +content="" + +# Read the CHANGELOG.md line by line +while IFS= read -r line +do + # Check if the line contains a version number + if [[ $line =~ "## ["[0-9]+\.[0-9]+\.[0-9]+"]" ]]; then + highest_version_with_date=$(grep -E '## \[[0-9]+\.[0-9]+\.[0-9]+\]' CHANGELOG.md | awk -F '[\\[\\]]' '{print $2}' | sort -Vr | head -n 1) + + # If a version was previously found, write the content to the corresponding file + if [ ! -z "$version" ]; then + lines="---\n" + lines+="layout: post\n" + lines+="title: \"Release $version\"\n" + lines+="date: $date 00:00:00 +0000\n" + lines+="categories: release notes\n" + lines+="---\n" + lines+="\n" + lines+="# Whats New" + echo -e "$lines" > "./docs/_posts/${date}-v${version}.markdown" + echo -e "$content" >> "./docs/_posts/${date}-v${version}.markdown" + fi + + # Extract the new version number, date and reset the content + version=$(echo $line | awk -F '[\\[\\]]' '{print $2}') + date=$(echo $line | awk '{print $NF}') + content="" + else + # Append the line to the content + content+="$line\n" + fi +done < "CHANGELOG.md" + +# Write the content for the last version +if [ ! -z "$version" ]; then + highest_version_with_date=$(grep -E '## \[[0-9]+\.[0-9]+\.[0-9]+\]' CHANGELOG.md | awk -F '[\\[\\]]' '{print $2}' | sort -Vr | head -n 1) + date_of_highest_version=$(echo $highest_version_with_date | awk '{print $NF}') + + lines="---\n" + lines+="layout: post\n" + lines+="title: \"Release $version\"\n" + lines+="date: $date 00:00:00 +0000\n" + lines+="categories: release notes\n" + lines+="---\n" + lines+="\n" + lines+="# Whats New\n" + lines+="$content" + echo -e "$lines" > "./docs/_posts/${date}-v${version}.markdown" +fi \ No newline at end of file diff --git a/.github/workflow_scripts/generate-changelog.sh b/.github/workflow_scripts/generate-changelog.sh new file mode 100755 index 0000000..31555e0 --- /dev/null +++ b/.github/workflow_scripts/generate-changelog.sh @@ -0,0 +1,242 @@ +#!/bin/bash + +VERBOSE="FALSE" +CHANGELOG_FILE="CHANGELOG.md" +RELEASE_NOTES_FILE="release_notes.md" +OUTPUT_TO_FILE="FALSE" +VERBOSE="FALSE" +MODE="GENERATE" +while [[ $# -gt 0 ]]; do + case $1 in + -m) + MODE=$2 + shift + shift + ;; + --mode) + MODE=$2 + shift + shift + ;; + -v) + NEW_RELEASE=$2 + shift + shift + ;; + --version) + NEW_RELEASE=$2 + shift + shift + ;; + -r) + REPO_NAME=$2 + shift + shift + ;; + --repo) + REPO_NAME=$2 + shift + shift + ;; + --CHANGELOG_FILE) + CHANGELOG_FILE=$2 + shift + shift + ;; + --file) + RELEASE_NOTES_FILE=$2 + shift + shift + ;; + --output-to-file) + OUTPUT_TO_FILE="TRUE" + shift + ;; + --verbose) + VERBOSE="TRUE" + shift + ;; + *) + echo "Invalid argument: $1" >&2 + exit 1 + ;; + esac +done + +function generate_release_notes() { + #get when the last release was merged + LAST_RELEASE_MERGED_AT=$(gh pr list --repo "$REPO_NAME" --base main --json mergedAt --state merged --search "label:release-request" | jq -r '.[0].mergedAt') + CHANGELIST=$(gh pr list --repo "$REPO_NAME" --base main --state merged --json body --search "merged:>$LAST_RELEASE_MERGED_AT -label:release-request") + + temp_file=$(mktemp) + + CONTENT=$(echo "$CHANGELIST" | jq -r '.[].body') + echo "$CONTENT" | while read -r line; do + if [[ $line != -* ]]; then + echo "- $line" >>"$temp_file" + else + echo "$line" >>"$temp_file" + fi + done + + content=$(awk '/# Description/{flag=1; next} /##/{flag=0} flag' "$temp_file") + # Trim empty lines from the content + content=$(echo "$content" | sed '/^[[:space:]]*$/d' | sed '/^-\s*$/d') + + if [ "$OUTPUT_TO_FILE" == "TRUE" ]; then + # store the release notes in a variable so we can use it later + if [ -f "$RELEASE_NOTES_FILE" ]; then + rm "$RELEASE_NOTES_FILE" + fi + echo "# Release $NEW_RELEASE" >"$RELEASE_NOTES_FILE" + echo "" >>"$RELEASE_NOTES_FILE" + echo "$content" >>"$RELEASE_NOTES_FILE" + else + echo "$content" + fi + + rm "$temp_file" +} + +function insert_changelog_content() { + local line_number="$1" + local content="$2" + local file="$3" + + local temp_file + temp_file=$(mktemp) + local content_file + content_file=$(mktemp) + + echo "$content" >"$content_file" + + line_number=$((line_number + 3)) + + awk -v lineno="$line_number" -v content_file="$content_file" ' + NR == lineno { + while ((getline line < content_file) > 0) { + print line + } + close(content_file) + } + { print } + ' "$file" >"$temp_file" + + mv "$temp_file" "$file" + rm "$content_file" +} + +function append_changelog_content() { + local start_line="$1" + local end_line="$2" + local content="$3" + local file="$4" + + local temp_file + temp_file=$(mktemp) + local content_file + content_file=$(mktemp) + + echo "$content" >"$content_file" + end_line=$((end_line - 1)) + + awk -v start="$start_line" -v end="$end_line" -v content_file="$content_file" ' + { + print + if (NR == end) { + while ((getline line < content_file) > 0) { + print line + } + close(content_file) + } + } + ' "$file" >"$temp_file" + + mv "$temp_file" "$file" + rm "$content_file" +} + +function generate_changelog_entry() { + TEMP_FILE=$(mktemp) + if [ "$VERBOSE" == "TRUE" ]; then + echo "Generating release notes for repository ${REPO_NAME}" + fi + generate_release_notes >>$TEMP_FILE + if [ "$VERBOSE" == "TRUE" ]; then + echo "Release notes generated successfully" + fi + CONTENT=$(cat "$TEMP_FILE") + + # Check if the version exists + VERSION_LINE=$(grep -n "^## \[$NEW_RELEASE\]" "$CHANGELOG_FILE" | cut -d: -f1) + if [ -z "$VERSION_LINE" ]; then + # Version does not exist, create a new section + if [ "$VERBOSE" == "TRUE" ]; then + echo "Version $NEW_RELEASE does not exist, creating new section" + fi + + # Find where to insert the new version (after the header) + HEADER_END_LINE=$(grep -n -m1 "^## \[.*\]" "$CHANGELOG_FILE" | cut -d: -f1) + if [ -z "$HEADER_END_LINE" ]; then + # No existing versions, append at the end + INSERT_LINE=$(wc -l <"$CHANGELOG_FILE") + INSERT_LINE=$((INSERT_LINE + 1)) + else + # Insert after the header (assumed to be the first two lines) + INSERT_LINE=3 + fi + + TODAY=$(date '+%Y-%m-%d') + + NEW_VERSION_SECTION="## [$NEW_RELEASE] - $TODAY + +$CONTENT +" + + insert_changelog_content "$INSERT_LINE" "$NEW_VERSION_SECTION" "$CHANGELOG_FILE" + else + # Version exists, append content to the version section + if [ "$VERBOSE" == "TRUE" ]; then + echo "Version $NEW_RELEASE exists, appending content" + fi + + # Find where the version section ends + NEXT_VERSION_LINE=$(awk -v ver_line="$VERSION_LINE" 'NR > ver_line && /^## \[.*\]/ {print NR; exit}' "$CHANGELOG_FILE") + + if [ -z "$NEXT_VERSION_LINE" ]; then + # Version section goes to the end of the file + END_LINE=$(wc -l <"$CHANGELOG_FILE") + else + END_LINE=$((NEXT_VERSION_LINE - 1)) + fi + + append_changelog_content "$VERSION_LINE" "$END_LINE" "$CONTENT" "$CHANGELOG_FILE" + fi + + # Remove the temporary file + rm "$TEMP_FILE" + if [ "$VERBOSE" == "TRUE" ]; then + echo "Changelog has been updated successfully." + fi +} + +if [ "$MODE" == "GENERATE" ]; then + if [ "$VERBOSE" == "TRUE" ]; then + echo "Generating changelog entry for repository ${REPO_NAME}" + fi + generate_changelog_entry + if [ "$VERBOSE" == "TRUE" ]; then + echo "Changelog entry generated successfully" + fi +elif [ "$MODE" == "RELEASE" ]; then + if [ "$VERBOSE" == "TRUE" ]; then + echo "Generating release notes for repository ${REPO_NAME}" + fi + generate_release_notes + if [ "$VERBOSE" == "TRUE" ]; then + echo "Release notes generated successfully" + fi +else + echo "Invalid mode: $MODE" >&2 + exit 1 +fi diff --git a/.github/workflow_scripts/generate-release-notes.sh b/.github/workflow_scripts/generate-release-notes.sh new file mode 100755 index 0000000..0476d54 --- /dev/null +++ b/.github/workflow_scripts/generate-release-notes.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# this script is used to generate release notes for a given release +# first argument is the repository name +# the secon is the pull request id for the last release +# and the third is the new release number + +# the script then grabs every pull request merged since that pull request +# and outputs a string of release notes + +echo "Generating release notes for $2" + +# get the repository name +REPO_NAME=$1 + +# get the last release pull request id +LAST_RELEASE_PR=$2 + +# get the new release number +NEW_RELEASE=$3 + +#get when the last release was merged +LAST_RELEASE_MERGED_AT=$(gh pr view "$LAST_RELEASE_PR" --repo "$REPO_NAME" --json mergedAt | jq -r '.mergedAt') + +CHANGELIST=$(gh pr list --repo "$REPO_NAME" --base main --state merged --json title --search "merged:>$LAST_RELEASE_MERGED_AT -label:no-release") + +# store the release notes in a variable so we can use it later + +echo "Release $NEW_RELEASE" >>releasenotes.md + +echo "$CHANGELIST" | jq -r '.[].title' | while read -r line; do + echo " - $line" >>releasenotes.md +done + +echo " " diff --git a/.github/workflow_scripts/get-latest-changelog-version.sh b/.github/workflow_scripts/get-latest-changelog-version.sh new file mode 100755 index 0000000..f5960d2 --- /dev/null +++ b/.github/workflow_scripts/get-latest-changelog-version.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# Use grep to extract lines with version numbers, cut to get the version numbers, sort them and get the highest +highest_version=$(grep -E '## \[[0-9]+\.[0-9]+\.[0-9]+\]' CHANGELOG.md | cut -d '[' -f 2 | cut -d ']' -f 1 | sort -Vr | head -n 1) + +echo $highest_version \ No newline at end of file diff --git a/.github/workflow_scripts/get-latest-changelog.sh b/.github/workflow_scripts/get-latest-changelog.sh new file mode 100755 index 0000000..b2abfc5 --- /dev/null +++ b/.github/workflow_scripts/get-latest-changelog.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +CHANGELOG_FILE="CHANGELOG.md" +OUTPUT_FILE="release_notes.md" +OUTPUT_TO_FILE="FALSE" +MODE="GENERATE" + +while [[ $# -gt 0 ]]; do + case $1 in + -m) + MODE=$2 + shift + shift + ;; + --mode) + MODE=$2 + shift + shift + ;; + --CHANGELOG_FILE) + CHANGELOG_FILE=$2 + shift + shift + ;; + --file) + OUTPUT_FILE shift + shift + ;; + --output-to-file) + OUTPUT_TO_FILE="TRUE" + shift + ;; + *) + echo "Invalid argument: $1" >&2 + exit 1 + ;; + esac +done + +function get_highest_version() { + # Use grep to extract lines with version numbers, cut to get the version numbers, sort them and get the highest + highest_version=$(grep -E '## \[[0-9]+\.[0-9]+\.[0-9]+\]' CHANGELOG.md | cut -d '[' -f 2 | cut -d ']' -f 1 | sort -Vr | head -n 1) + echo $highest_version +} + +function get_content_for_version() { + # Check if a version was found + if [ -z "$highest_version" ]; then + echo "No version found in the changelog." + exit 1 + fi + + # Extract the content for the highest version + awk ' + /^## \[.*\]/ { + if (found_version) { + exit + } else { + found_version=1 + next + } + } + found_version { + print + } +' "$CHANGELOG_FILE" +} + +function generate_release_notes() { + # Get the highest version + highest_version=$(get_highest_version) + + # Get the content for the highest version + content=$(get_content_for_version) + + # Write the content to the output file + if [ "$OUTPUT_TO_FILE" == "TRUE" ]; then + echo -e "# Release Notes for v$highest_version\n$content" >$OUTPUT_FILE + else + echo -e "# Release Notes for v$highest_version\n$content" + fi +} + +if [ "$MODE" == "GENERATE" ]; then + generate_release_notes +elif [ "$MODE" == "HIGHEST_VERSION" ]; then + get_highest_version +else + echo "Invalid mode: $MODE" >&2 + exit 1 +fi diff --git a/.github/workflow_scripts/increment-version.sh b/.github/workflow_scripts/increment-version.sh new file mode 100755 index 0000000..343340c --- /dev/null +++ b/.github/workflow_scripts/increment-version.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +while getopts ":t:f:" opt; do + case $opt in + t) + TYPE=$OPTARG + ;; + f) + FILE=$OPTARG + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + esac +done + +if [ -z "$FILE" ]; then + echo "You need to specify the version file with the -f flag" +fi + +VERSION=$(cat "$FILE") + +MAJOR=$(echo "$VERSION" | cut -d. -f1) +MINOR=$(echo "$VERSION" | cut -d. -f2) +PATCH=$(echo "$VERSION" | cut -d. -f3) + +if [ "$TYPE" == "major" ]; then + MAJOR=$((MAJOR + 1)) + MINOR=0 + PATCH=0 +elif [ "$TYPE" == "minor" ]; then + MINOR=$((MINOR + 1)) + PATCH=0 +elif [ "$TYPE" == "patch" ]; then + PATCH=$((PATCH + 1)) +else + echo "Invalid version type. Use 'major', 'minor' or 'patch'" + exit 1 +fi + +NEW_VERSION="$MAJOR.$MINOR.$PATCH" +echo "$NEW_VERSION" diff --git a/CHANGELOG.md b/CHANGELOG.md index bf07f6d..eeeb1cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,18 @@ -## v0.2.2 +# Changelog -* Fixed an issue that was preventing the deployment from linux machines -* Removed some unnecessary files - -## 0.2.0 +All notable changes to this project will be documented in this file. +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.2.2] - 2023-01-01 + +- Fixed an issue that was preventing the deployment from linux machines +- Removed some unnecessary files + +## [0.2.0] - 2023-01-01 FEATURES: -* Fixed some bugs -* Added the ability to register a deployment with a orchestrator -* Added the ability to run prlctl commands -* Added the ability to run a script on destroy \ No newline at end of file +- Fixed some bugs +- Added the ability to register a deployment with a orchestrator +- Added the ability to run prlctl commands +- Added the ability to run a script on destroy diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..5cd6428 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.4.9 \ No newline at end of file