From c98d16b5424fb0c55c946460efd027eac08c287e Mon Sep 17 00:00:00 2001 From: Austin Gill Date: Sun, 7 Apr 2024 11:38:06 -0500 Subject: [PATCH 1/5] ci: Make automated release when tags are pushed --- .github/get-release-notes.sh | 100 ++++++++++++++++++++++++++++++++++ .github/parse-manifest-key.sh | 15 +++++ .github/workflows/release.yml | 28 ++++++++++ CHANGELOG.md | 14 ++--- 4 files changed, 150 insertions(+), 7 deletions(-) create mode 100755 .github/get-release-notes.sh create mode 100755 .github/parse-manifest-key.sh create mode 100644 .github/workflows/release.yml diff --git a/.github/get-release-notes.sh b/.github/get-release-notes.sh new file mode 100755 index 0000000..8142c10 --- /dev/null +++ b/.github/get-release-notes.sh @@ -0,0 +1,100 @@ +#!/bin/bash +set -o errexit +set -o pipefail +set -o nounset +set -o noclobber + +RED="\033[31m" +GREEN="\033[32m" +BLUE="\033[34m" +RESET="\033[0m" + +debug() { + echo -e "${BLUE}DEBUG:${RESET} $*" >&2 +} + +info() { + echo -e "${GREEN}INFO:${RESET} $*" >&2 +} + +error() { + echo -e "${RED}ERROR:${RESET} $*" >&2 +} + +usage() { + echo "Usage: $0 [--help] " + echo + echo "Positional Arguments:" + echo + echo " The version for which to generate the release notes" + echo " The project description, because every release notes should describe" + echo " what the project is for new users" + echo + echo "Options:" + echo + echo " --help, -h Show this help and exit" +} + +parse_changelog() { + local -r changelog="$1" + local -r version="$2" + + # 0,/pat1/d deletes from line 1 to pat1 inclusive + # /pat2/Q exits without printing on the first line to match pat2 + sed "0,/^# Herostratus - $version/d;/^# /Q" "$changelog" +} + +main() { + local version="" + local description="" + + while [[ $# -gt 0 ]]; do + case "$1" in + --help | -h) + usage + exit 0 + ;; + -*) + error "Unexpected option: $1" + usage >&2 + exit 1 + ;; + *) + if [[ -z "$version" ]]; then + version="$1" + elif [[ -z "$description" ]]; then + description="$1" + fi + ;; + esac + shift + done + + if [[ -z "$version" ]]; then + error "Missing required positional argument" + exit 1 + elif [[ -z "$description" ]]; then + error "Missing required positional argument" + exit 1 + fi + + local repo_dir + repo_dir=$(git rev-parse --show-toplevel) + local -r changelog="$repo_dir/CHANGELOG.md" + if [[ ! -f "$changelog" ]]; then + error "Could not find '$changelog'" + exit 1 + fi + + # This outputs the version header to stdout + if ! grep "^# Herostratus - $version" "$changelog"; then + error "Could not find version '$version' in '$changelog'" + exit 1 + fi + # Add the project description to the release notes + echo "$description" + # This outputs from the version header (exclusive) to the next version header (exclusive) + parse_changelog "$changelog" "$version" +} + +main "$@" diff --git a/.github/parse-manifest-key.sh b/.github/parse-manifest-key.sh new file mode 100755 index 0000000..c5eb35f --- /dev/null +++ b/.github/parse-manifest-key.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -o errexit +set -o pipefail +set -o nounset +set -o noclobber + +CARGO_MANIFEST="${1:-Cargo.toml}" +MANIFEST_KEY="${2:-version}" + +cargo metadata \ + --format-version=1 \ + --manifest-path "$CARGO_MANIFEST" \ + --no-deps | + jq ".packages[0].$MANIFEST_KEY" | + tr -d '"' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..058e285 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,28 @@ +name: Release + +on: + push: + tags: + - "v*.*.*" + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Extract Release Metadata + shell: bash + run: | + VERSION="$(.github/parse-manifest-key.sh Cargo.toml version)" + DESCRIPTION="$(.github/parse-manifest-key.sh Cargo.toml description)" + echo "VERSION=$VERSION" >> "$GITHUB_ENV" + echo "DESCRIPTION=$DESCRIPTION" >> "$GITHUB_ENV" + - name: Generate Release Notes + shell: bash + run: .github/get-release-notes.sh "$VERSION" "$DESCRIPTION" | tee release.md + - name: Make The Release + uses: softprops/action-gh-release@v2 + with: + body_path: release.md + prerelease: ${{ contains(env.VERSION, '-rc') }} + token: ${{ secrets.HEROSTRATUS_RELEASE_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 130a486..3e02953 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,12 +8,12 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). These changelog entries will automatically be added to the project release notes. **Please keep the focus on the user impact** rather than the actual changes made. -## Unreleased - (YYYY-MM-DD) +# Herostratus - Unreleased - (YYYY-MM-DD) -### Added -### Changed -### Deprecated -### Removed -### Fixed -### Security +## Added +## Changed +## Deprecated +## Removed +## Fixed +## Security From 7c76dbc378cc5100651e3edd76ffb980685bdce5 Mon Sep 17 00:00:00 2001 From: Austin Gill Date: Sun, 7 Apr 2024 12:14:43 -0500 Subject: [PATCH 2/5] ci: Switch from 'pull_request' events to 'push' --- .github/workflows/lint.yml | 14 +++++++------- .github/workflows/release.yml | 11 +++++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7379554..dda07b6 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,5 +1,5 @@ name: Lint -on: [pull_request] +on: push env: RUSTFLAGS: -D warnings CARGO_TERM_COLOR: always @@ -10,18 +10,18 @@ jobs: steps: - uses: actions/checkout@v4 with: - # Check out at the last commit (pre-automated merge, we don't care about the - # temporary commit for linting) - ref: ${{ github.event.pull_request.head.sha }} - # Get all history - fetch-depth: 0 + # PRs (hopefully??!) won't have more than 20 commits + fetch-depth: 20 - name: Install gitlint run: python -m pip install gitlint - name: Run gitlint env: - GITLINT_COMMIT_RANGE: "${{ github.event.pull_request.base.sha }}..HEAD" + # Don't use github.event.pull_request.base-sha, because that requires a workflow + # event of 'pull_request' which precludes running this workflow on 'main'. + GITLINT_COMMIT_RANGE: "origin/main..HEAD" run: | echo "GITLINT_COMMIT_RANGE=$GITLINT_COMMIT_RANGE" + git fetch origin main:main git log --color=always --graph --decorate --oneline "$GITLINT_COMMIT_RANGE" gitlint \ --ignore-stdin \ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 058e285..773b34e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,6 @@ name: Release -on: - push: - tags: - - "v*.*.*" +on: push jobs: release: @@ -17,11 +14,17 @@ jobs: DESCRIPTION="$(.github/parse-manifest-key.sh Cargo.toml description)" echo "VERSION=$VERSION" >> "$GITHUB_ENV" echo "DESCRIPTION=$DESCRIPTION" >> "$GITHUB_ENV" + + # Always generate release notes for the current version. If the version from the Cargo + # manifest isn't found in the CHANGELOG.md, then fail the pipeline (even in PRs) - name: Generate Release Notes shell: bash run: .github/get-release-notes.sh "$VERSION" "$DESCRIPTION" | tee release.md + - name: Make The Release uses: softprops/action-gh-release@v2 + # Only actually make the release on a tag + if: startsWith(github.ref, 'refs/tags/') with: body_path: release.md prerelease: ${{ contains(env.VERSION, '-rc') }} From f7057572b70f5429f87edf6a1081f19533db50f6 Mon Sep 17 00:00:00 2001 From: Austin Gill Date: Sun, 7 Apr 2024 13:10:37 -0500 Subject: [PATCH 3/5] ci: Make release tag when version is bumped --- .github/check-if-tag-already-exists.sh | 197 ++++++++++++++++++ .github/parse-semver-version.sh | 58 ++++++ .../validate-version-is-semver-compliant.sh | 74 +++++++ .github/workflows/release.yml | 48 ++++- CONTRIBUTING.md | 16 +- 5 files changed, 388 insertions(+), 5 deletions(-) create mode 100755 .github/check-if-tag-already-exists.sh create mode 100755 .github/parse-semver-version.sh create mode 100755 .github/validate-version-is-semver-compliant.sh diff --git a/.github/check-if-tag-already-exists.sh b/.github/check-if-tag-already-exists.sh new file mode 100755 index 0000000..49d35af --- /dev/null +++ b/.github/check-if-tag-already-exists.sh @@ -0,0 +1,197 @@ +#!/bin/bash + +set -o errexit +set -o pipefail +set -o nounset +set -o noclobber + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +usage() { + cat < + +Check if a git tag exists in the current repo that should prevent creating a +specific new tag. + +--help, -h Show this help and exit +--tag-prefix, -t Tag prefix. Defaults to 'v' +--branch-prefix, -b Release branch prefix. Defaults to 'release/' +--target-branch Target branch. Defaults to 'master' + +The following checks are performed +* A tag which is equivalent to the target version does not exist +* A non-prerelease tag that matches the target version does not already exist +* A separate release branch does not exist for the major.minor of the target + version + +A semantic version is defined as equivalent if the major, minor, patch +and prerelease information are all the same. This means that two versions may +not be differentiated by only the build metadata, regardless of if only one of +the versions being compared contains it. + +Exits with non-zero if you should not be allowed to create the target version +EOF +} + +check_if_tag_already_exists() { + local -n components="$1" + local tag_prefix="$2" + + # If a tag matching the prerelease and/or metadata exactly is found, then don't allow + # + # If a tag with prerelease and/or metadata is found, but our current version has a different + # prerelease/metadata component, allow it + # + # If there is no prerelease/metadata and the tag matches exactly, then don't allow + # + # Allowed: + # Existing | New + # --------------|----------- + # v1.0.0 | v0.0.1 + # v1.0.0 | v0.0.0-rc1 + # v1.0.0-rc1 | v1.0.0-rc2 + # v1.0.0-rc1+g1 | v1.0.0-rc2+g1 + # + # Disallowed: + # Existing | New + # -------------|------------ + # v1.0.0 | v1.0.0 + # v1.0.0-rc1 | v1.0.0-rc1 + # v1.0.0+g1234 | v1.0.0+gabcd + + # If you're making a prerelease, check to see that a "real" release hasn't been made, and that + # no duplicate prerelease has been made + if [[ -n "${components[prerelease]}" ]]; then + local pattern="${tag_prefix}${components[major]}.${components[minor]}.${components[patch]}-${components[prerelease]}" + local tags + tags="$(git for-each-ref --format='%(refname:short)' "refs/tags/${pattern}*")" + if [[ -n "$tags" ]]; then + echo "Found existing prerelease tags:" >&2 + for tag in $tags; do + echo " $tag" >&2 + done + exit 1 + fi + + # Also check if there's a "real" release for that version + pattern="${tag_prefix}${components[major]}.${components[minor]}.${components[patch]}" + tags="$(git for-each-ref --format='%(refname:short)' "refs/tags/${pattern}")" + if [[ -n "$tags" ]]; then + echo "You tried to make a prerelease of a version that was already released:" >&2 + for tag in $tags; do + echo " $tag" >&2 + done + exit 1 + fi + tags="$(git for-each-ref --format='%(refname:short)' "refs/tags/${pattern}+*")" + if [[ -n "$tags" ]]; then + echo "You tried to make a prerelease of a version that was already released:" >&2 + for tag in $tags; do + echo " $tag" >&2 + done + exit 1 + fi + # If there's no prerelease, we can't even re-use the same major.minor.patch, even if the build + # metadata is different + else + local pattern="${tag_prefix}${components[major]}.${components[minor]}.${components[patch]}" + local tags + tags="$(git for-each-ref --format='%(refname:short)' "refs/tags/${pattern}")" + if [[ -n "$tags" ]]; then + echo "Found existing release tags:" >&2 + for tag in $tags; do + echo " $tag" >&2 + done + exit 1 + fi + tags="$(git for-each-ref --format='%(refname:short)' "refs/tags/${pattern}+*")" + if [[ -n "$tags" ]]; then + echo "Found existing release tag with metadata:" >&2 + for tag in $tags; do + echo " $tag" >&2 + done + exit 1 + fi + fi + +} + +check_if_maintenance_release_made_on_non_maintenance_branch() { + local -n components="$1" + local branch_prefix="$2" + local target_branch="$3" + + local major_minor="${components[major]}.${components[minor]}" + + # Has there been a release branch for the specified version been made? + local branches + branches="$(git for-each-ref --format='%(refname:short)' "refs/heads/${branch_prefix}${major_minor}*")" + if [[ -n "$branches" ]]; then + echo "Found release branch for $major_minor: ${branches}" + for branch in $branches; do + if [[ "$branch" = "$target_branch" ]]; then + echo "...but that branch was the target branch, so that's okay." + return 0 + fi + done + echo "If a release branch has been made for $major_minor, you can't release a new $major_minor version unless done from that release branch" + exit 1 + fi +} + +main() { + local version="" + local tag_prefix="v" + local branch_prefix="release/" + local target_branch="main" + + while test $# -gt 0; do + case "$1" in + --help | -h) + usage + exit + ;; + --tag-prefix | -t) + tag_prefix="$2" + shift + ;; + --branch-prefix | -b) + branch_prefix="$2" + shift + ;; + --target-branch) + target_branch="$2" + shift + ;; + *) + if [[ -z "$version" ]]; then + version="$1" + else + echo "Unexpected argument '$1'" >&2 + exit 1 + fi + ;; + esac + shift + done + + if [[ -z "$version" ]]; then + echo "Missing required argument" >&2 + exit 1 + fi + + # shellcheck disable=SC1091 + source "${SCRIPT_DIR}/parse-semver-version.sh" + + # shellcheck disable=SC2034 + # nameref array passing confuses shellcheck + local -A version_components + # Exits the script if it fails to parse the version components + get_tag_semver_components "$version" version_components + + check_if_tag_already_exists version_components "$tag_prefix" + check_if_maintenance_release_made_on_non_maintenance_branch version_components "$branch_prefix" "$target_branch" +} + +main "$@" diff --git a/.github/parse-semver-version.sh b/.github/parse-semver-version.sh new file mode 100755 index 0000000..05f26e7 --- /dev/null +++ b/.github/parse-semver-version.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# shellcheck disable=SC2154,SC2034 + +# Function to parse the components of a semantic version string with optional +# prefix. +# +# Params: +# $1 - The string to parse +# $2 - The name of the variable to store the output in +# +# Returns: +# The resulting components are stored in an associative array named according +# to the second argument passed to the function. (See usage example) +# +# Exits with code 1 if the given text cannot be parsed +# +# Usage Example: +# +# source ./parse-semver-version.sh +# +# local -A tag_components +# get_tag_semver_components v1.2.3-a+b tag_components +# +# echo "$tag_components[prefix]" +# echo "$tag_components[major].$tag_components[minor].$tag_components[patch]" +# echo "$tag_components[prerelease] -- $tag_components[buildmetadata]" +get_tag_semver_components() { + + local tag="$1" + # This is a "nameref" variable. $2 holds the name of the variable to update. + # We declare it as an associative array in the calling function. + local -n components="$2" + + # Taken from https://gist.github.com/rverst/1f0b97da3cbeb7d93f4986df6e8e5695, which itself is a + # form of the PCRE regex from https://semver.org modified to work with Bash regular expressions, + # and to allow an optional prefix for v1.0.0 style Git tags. + # + # Use the string "v1.2.3-0.ab.0001a.a+gabcd.xyz" to test. + # + # This parses the tag into the following capture groups: + # 1 2 3 4 5 67 8 9 10 11 12 + # (prefix )?(major ).(minor ).(patch )(-((prerelease )( ( )) ))?( +(buildmetadata( ) ))? + local regex='^([a-zA-Z_/-]+)?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$' + + if [[ $tag =~ $regex ]]; then + components[prefix]="${BASH_REMATCH[1]}" + components[major]="${BASH_REMATCH[2]}" + components[minor]="${BASH_REMATCH[3]}" + components[patch]="${BASH_REMATCH[4]}" + components[prerelease]="${BASH_REMATCH[6]}" + components[buildmetadata]="${BASH_REMATCH[11]}" + else + echo "Failed to parse SemVer 2.0.0 tag '$tag'" >&2 + exit 1 + fi + + # The "components" array is "returned" via namerefs, similar to pass-by-reference in C++. +} diff --git a/.github/validate-version-is-semver-compliant.sh b/.github/validate-version-is-semver-compliant.sh new file mode 100755 index 0000000..8c6159f --- /dev/null +++ b/.github/validate-version-is-semver-compliant.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +set -o errexit +set -o pipefail +set -o nounset +set -o noclobber + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +usage() { + cat < + +The script will exit with non-zero status if it fails to parse the given version number. + +Note - semver doesn't actually define any rules relating to a prefix; but we use pretty +frequently internally (e.g. making a tag like vx.y.z), so this script supports finding +a prefix before a valid semantic version. + +If the version does parse, then the different version components will be spit out in the following +format: + + $ $0 v2.3.1-rc2+g1234 + prefix: v + major: 2 + minor: 3 + patch: 1 + prerelease: rc2 + buildmetadata: g1234 +EOF +} + +main() { + local version="" + while test $# -gt 0; do + case "$1" in + --help | -h) + usage + exit + ;; + *) + if [[ -z "$version" ]]; then + version="$1" + else + echo "Unexpected argument '$1'" >&2 + exit 1 + fi + ;; + esac + shift + done + + if [[ -z "$version" ]]; then + echo "Missing required argument" >&2 + exit 1 + fi + + # shellcheck disable=SC1091 + source "${SCRIPT_DIR}/parse-semver-version.sh" + + # shellcheck disable=SC2034 + local -A version_components + # Exits the script if it fails to parse the version components + get_tag_semver_components "$version" version_components + + echo "prefix: ${version_components[prefix]}" + echo "major: ${version_components[major]}" + echo "minor: ${version_components[minor]}" + echo "patch: ${version_components[patch]}" + echo "prerelease: ${version_components[prerelease]}" + echo "buildmetadata: ${version_components[buildmetadata]}" +} + +main "$@" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 773b34e..4ff1e48 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,55 @@ name: Release +# This workflow is not ordered after the 'Lint' workflow. And doing so is difficult, because +# workflow_run: only applies on the default branch, and I EXPRESSLY WANT this workflow to run in PRs +# too, because it validates some preconditions for making the release. +# +# So make the assumption that if the PR pipeline passed, and this workflow runs on the default +# branch, it's okay to make a release without waiting for the build and test on: push jobs: - release: + trigger-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.HEROSTRATUS_RELEASE_TOKEN }} + - name: Validate SemVer Version + shell: bash # Explicitly setting the shell sets errexit and pipefail flags + run: | + VERSION="$(.github/parse-manifest-key.sh Cargo.toml version)" + echo "Validating SemVer version '$VERSION' ..." + .github/validate-version-is-semver-compliant.sh "$VERSION" + echo "VERSION=$VERSION" >> "$GITHUB_ENV" + + - name: Check if current version has a Git tag + shell: bash + if: github.ref == 'refs/head/main' + run: |- + # Delete any local tags cached by the runner, and fetch remote tags to ensure we're + # up-to-date. + git tag --list | xargs git tag --delete + git fetch --force --tags + + # Exits with success if tag does not exist, failure if tag _does_ exist. + if ! .github/check-if-tag-already-exists.sh \ + --tag-prefix 'v' \ + --target-branch 'main' \ + "$VERSION" \ + ; then + echo "Found tag for current version '$VERSION'. Skipping release ..." + else + echo "Could not find tag for current version '$VERSION'. Making relese ..." + git config --global user.name "${{ github.actor }}" + git config --global user.email "${{ github.actor }}@users.noreply.github.com" + RELEASE_TAG="v$VERSION" + git tag --annotate --message "Auto-generated release tag $RELEASE_TAG generated by GitHub Action run by ${{ github.actor }}" "$RELEASE_TAG" + git push origin "refs/tags/$RELEASE_TAG" + fi + + # Run all jobs except "Make The Release" on every push event + generate-release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1462e44..e117ac5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,13 +25,21 @@ but every notable feature should be added. **Please keep the changelog focused on the user impact, instead of the actual changes made.** +These changelog entries are automatically added to the generated releases: + + ## Releases Releases are automated by the CI/CD pipeline, and are triggered by merging a change that bumps the -version number in the project [Cargo.toml](Cargo.toml). Each release is required to have an entry in -the changelog. - -**TODO:** Add automated release pipeline. +version number in the project [Cargo.toml](Cargo.toml). + +The process for making a new release is: +1. Submit a PR making the release + 1. Bump the version number in `Cargo.toml` + 2. Add the new version number to the `CHANGELOG.md`, moving all of the entries under the `# + Herostratus - Unreleased - (YYYY-MM-DD)` header down under the header for the new release. + 3. Perform any last-minute release notes polish +2. Merge it! ## Deployment From 1856992f4cdd612ba7aa76aa9c1b438505513657 Mon Sep 17 00:00:00 2001 From: Austin Gill Date: Sun, 7 Apr 2024 12:17:57 -0500 Subject: [PATCH 4/5] Add CI/CD badges to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7a24073..ade7b75 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # Herostratus +![lint workflow](https://github.com/Notgnoshi/herostratus/actions/workflows/lint.yml/badge.svg?event=push) +![release workflow](https://github.com/Notgnoshi/herostratus/actions/workflows/release.yml/badge.svg?event=push) **Herostratus** *n.* **1.** An ancient Greek known for winning fame through crime and destruction. **2.** A Git repository achievements engine. From e5ca342f15ba548798dfad869efe4b5bdd80beb1 Mon Sep 17 00:00:00 2001 From: Austin Gill Date: Sun, 7 Apr 2024 11:40:48 -0500 Subject: [PATCH 5/5] Release Herostratus 0.1.0-rc1 --- CHANGELOG.md | 34 ++++++++++++++++++++++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 4 ++-- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e02953..08e3589 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,3 +17,37 @@ focus on the user impact** rather than the actual changes made. ## Removed ## Fixed ## Security + +# Herostratus - 0.1.0-rc1 - (2024-04-07) +This is the first release of Herostratus! This release is largely centered around project +bootstrapping; Herostratus isn't quite ready to use yet. + +## Added +* Cargo project bootstrapping. You can run Herostratus with + + ```sh + cargo run -- $CLONE_URL_OR_PATH $REF_OR_REV + ``` + * `$CLONE_URL_OR_PATH` can be any non-authenticated clone URL (https, or local file paths) + * `$REF_OR_REV` is typically the name of a reference to parse. Herostratus will resolve the + reference to a revision, and visit all reachable commits from the revision. + + The output is pretty bare bones. There's only a single achievement rule defined for prototyping + purposes. + + For example, you can run Herostratus on itself, using the + test branch + ```sh + $ cargo run -- . origin/test/fixup + Achievement { name: "I meant to fix that up later, I swear!", commit: 2721748d8fa0b0cc3302b41733d37e30161eabfd } + Achievement { name: "I meant to fix that up later, I swear!", commit: a987013884fc7dafbe9eb080d7cbc8625408a85f } + Achievement { name: "I meant to fix that up later, I swear!", commit: 60b480b554dbd5266eec0f2378f72df5170a6702 } + ``` + +* Automated CI/CD pipelines to build, test, and release Herostratus. + + The process to create a release is: + 1. Add release notes to the `CHANGELOG.md` + 2. Bump the version in `Cargo.toml` + + and the pipeline will do the rest! diff --git a/Cargo.lock b/Cargo.lock index 98cdbb3..8fb3e08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -344,7 +344,7 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "herostratus" -version = "0.1.0" +version = "0.1.0-rc1" dependencies = [ "assert_cmd", "clap", diff --git a/Cargo.toml b/Cargo.toml index aa32056..27b1573 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "herostratus" -description = "An ancient Greek known for winning fame through crime and destruction. Also a Git repository achievements engine" +description = "**Herostratus** *n.* **1.** An ancient Greek known for winning fame through crime and destruction. **2.** Also a Git repository achievements engine." repository = "https://github.com/Notgnoshi/herostratus" license = "MIT" -version = "0.1.0" +version = "0.1.0-rc1" edition = "2021" [dependencies]