diff --git a/.github/cliff.toml b/.github/cliff.toml new file mode 100644 index 0000000..7b0eb10 --- /dev/null +++ b/.github/cliff.toml @@ -0,0 +1,169 @@ +# git-cliff ~ configuration file +# https://git-cliff.org/docs/configuration + +[changelog] +# template for the changelog header +header = """ +# Changelog\n +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +\n +""" +# template for the changelog body +# https://keats.github.io/tera/docs/#introduction +body = """ +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} +{%- set init_commit = get_env(name="FIRST_COMMIT", default="") -%} +{%- set this_version = "Unreleased" -%} + +{% if version -%} + {%- set this_version = version | trim_start_matches(pat="v") -%} + ## [{{ this_version }}] - {{ timestamp | date(format="%Y-%m-%d") }} + {%- if message %} + + > {{ message }} + {%- endif %} +{% else -%} + ## [Unreleased] +{% endif -%} + +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | upper_first }} + {% for commit in commits | unique(attribute="message") %} + - {{ commit.message | split(pat="\n") | first | upper_first | trim }}\ + {% if commit.remote.username %} by \\@{{ commit.remote.username }}{%- endif -%} + {% if commit.remote.pr_number %} in \ + [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }}) + {%- else %} in \ + [`{{ commit.id | truncate(length=7, end="") }}`]({{ self::remote_url() }}/commit/{{commit.id }}) + {%- endif -%} + {% endfor %} +{% endfor -%} + +{% set first_commit = previous.version -%} +{%- set last_commit = "HEAD" -%} +{% if version -%} + {%- set last_commit = version -%} + {%- if not previous.version -%} + {%- set first_commit = init_commit -%} + {%- endif -%} +{%- endif %} +[{{ this_version }}]: {{ self::remote_url() }}/compare/{{ first_commit }}...{{ last_commit }} + +Full commit diff: [`{% if previous.version -%} + {{ first_commit }} +{%- else -%} + {{ init_commit | truncate(length=7, end="") }} +{%- endif %}...{{ last_commit }}`][{{ this_version }}] +{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %} + ## New Contributors + {% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %} + - @{{ contributor.username }} made their first contribution + {%- if contributor.pr_number %} in \ + [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \ + {%- endif %} + {%- endfor %} +{% endif %} + +""" +# template for the changelog footer +footer = """ + +""" +# remove the leading and trailing whitespace from the templates +trim = true +# The file path for output. This can be overridden with `--output` CLI arg +# output = "CHANGELOG.md" + +[git] +# parse the commits based on https://www.conventionalcommits.org +conventional_commits = true +# filter out the commits that are not conventional +filter_unconventional = false +# regex for preprocessing the commit messages +commit_preprocessors = [ + # Remove the auto-appended PR numbers from commit titles. + # For example, `some PR title (#42)` becomes `some PR title` + # NOTE: git-cliff will append a link to the commit or PR in the commit title. + # For example, `some PR title (#42)` gets displayed as + # `some PR title by in ` + { pattern = '(?m)(\s?\(#[0-9]+\))$', replace = "" }, +] +# regex for parsing and grouping commits +commit_parsers = [ + # The order of parsers matters. Put rules for PR labels first to prioritize PR labels. + { field = "github.pr_labels", pattern = "breaking", group = " 💥 Breaking changes" }, + { field = "github.pr_labels", pattern = "breaking-change", group = " 💥 Breaking changes" }, + { field = "github.pr_labels", pattern = "feature", group = " 🚀 New features and improvements" }, + { field = "github.pr_labels", pattern = "enhancement", group = " 🚀 New features and improvements" }, + { field = "github.pr_labels", pattern = "deprecated", group = " ⚠️ Deprecated" }, + { field = "github.pr_labels", pattern = "removed", group = " 🚨 Removed" }, + { field = "github.pr_labels", pattern = "bug", group = " 🐛 Bug fixes" }, + { field = "github.pr_labels", pattern = "bugfix", group = " 🐛 Bug fixes" }, + { field = "github.pr_labels", pattern = "fix", group = " 🐛 Bug fixes" }, + { field = "github.pr_labels", pattern = "regression", group = " 🐛 Bug fixes" }, + { field = "github.pr_labels", pattern = "security", group = " 🔐 Security" }, + { field = "github.pr_labels", pattern = "dependencies", group = " 📦 Dependency updates" }, + { field = "github.pr_labels", pattern = "test", group = " 🚦 Tests"}, + { field = "github.pr_labels", pattern = "tests", group = " 🚦 Tests"}, + { field = "github.pr_labels", pattern = "localization", group = " 🌐 Localization and translation"}, + { field = "github.pr_labels", pattern = "documentation", group = " 📝 Documentation" }, + { field = "github.pr_labels", pattern = "chore", group = " 👻 Maintenance" }, + { field = "github.pr_labels", pattern = "maintenance", group = " 👻 Maintenance" }, + { field = "github.pr_labels", pattern = "internal", group = " 👻 Maintenance" }, + { field = "github.pr_labels", pattern = "developer", group = " 👷 Changes for developers" }, + { field = "github.pr_labels", pattern = "refactor", group = " ✍ Other changes" }, + { field = "github.pr_labels", pattern = "skip-changelog", skip = true }, + { field = "github.pr_labels", pattern = "no-changelog", skip = true }, + { field = "github.pr_labels", pattern = "invalid", skip = true }, + # Here are rules that apply to conventional commits + { field = "group", pattern = "add", group = " 🚀 New features and improvements" }, + { field = "group", pattern = "feat", group = " 🚀 New features and improvements" }, + { field = "group", pattern = "fix", group = " 🐛 Bug fixes" }, + { field = "group", pattern = "perf", group = " ⚡ Performance" }, + { field = "group", pattern = "build", group = " 📦 Dependency updates" }, + { field = "group", pattern = "test", group = " 🚦 Tests" }, + { field = "group", pattern = "docs", group = " 📝 Documentation" }, + { field = "group", pattern = "chore", group = " 👻 Maintenance" }, + { field = "group", pattern = "style", group = " 👻 Maintenance" }, + { field = "breaking", pattern = true, group = " 💥 Breaking changes" }, + { field = "group", pattern = "remove", group = " 🚨 Removed" }, + { field = "group", pattern = "deprecate", group = " ⚠️ Deprecated" }, + { field = "group", pattern = "delete", group = " 🚨 Removed" }, + { field = "group", pattern = "security", group = " 🔐 Security" }, + { field = "group", pattern = "refactor", group = " ✍ Other changes" }, + # Here are rules that apply to unconventional commits + { message = "(?i)^add", group = " 🚀 New features and improvements" }, + { message = "(?i)^support", group = " 🚀 New features and improvements" }, + { message = "^.*: support", group = " 🚀 New features and improvements" }, + { message = "^.*: add", group = " 🚀 New features and improvements" }, + { message = "^.*: deprecated", group = " ⚠️ Deprecated" }, + { message = "(?i)deprecate", group = " ⚠️ Deprecated" }, + { message = "(?i)tests", group = " 🚦 Tests"}, + { message = "(?i)remove", group = " 🚨 Removed" }, + { message = "^.*: remove", group = " 🚨 Removed" }, + { message = "^.*: delete", group = " 🚨 Removed" }, + { message = "(?i)^fix", group = " 🐛 Bug fixes" }, + { message = "^.*: fix", group = " 🐛 Bug fixes" }, + { message = "^.*: secure", group = " 🔐 Security" }, + { message = "(?i)secure", group = " 🔐 Security" }, + { message = "(?i)security", group = " 🔐 Security" }, + { message = "^.*: security", group = " 🔐 Security" }, + { message = "doc", group = " 📝 Documentation" }, + { message = "docs", group = " 📝 Documentation" }, + { message = "documentation", group = " 📝 Documentation" }, + # group any un-matched commits into the "Other changes" category + { message = "(?i)refactor", group = " ✍ Other changes" }, + { field = "github.pr_labels", pattern = ".*", group = " ✍ Other changes" }, + { message = ".*", group = " ✍ Other changes" }, +] +# filter out the commits that are not matched by commit parsers +filter_commits = false +# sort the tags topologically +topo_order = false +# sort the commits inside sections by oldest/newest order +sort_commits = "oldest" diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e8d9778..4f37862 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -15,3 +15,11 @@ updates: actions: patterns: - "*" + - package-ecosystem: pip + directory: .github/ + schedule: + interval: "monthly" + groups: + pip: + patterns: + - "*" diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000..54878bf --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,25 @@ +# The config for actions/labeler. +# See https://github.com/actions/labeler for more details. + +documentation: + - all: + - changed-files: + - any-glob-to-any-file: + - "**/*.md" + - "**/*.rst" + - all-globs-to-all-files: + - "!CHANGELOG.md" + - any: + - head-branch: + - "^docs?" + - "docs?" +bug: + - head-branch: + - "^fix" + - "fix" +enhancement: + - head-branch: + - "^feature" + - "feature" + - "^feat" + - "feat" diff --git a/.github/requirements.txt b/.github/requirements.txt new file mode 100644 index 0000000..5e9e449 --- /dev/null +++ b/.github/requirements.txt @@ -0,0 +1 @@ +git-cliff==2.10.0 diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 0000000..4e96cae --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,77 @@ +name: Label PR + +on: + workflow_call: + +jobs: + update-pr-labels: + # Only execute this job when the PR is not from a fork. If the PR is from a fork, + # then the needed permission (pull-requests: write) is not granted. + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name }} + permissions: + # needed to read info like git refs and changed files + contents: read + # needed to make changes to a PR's labels + pull-requests: write + runs-on: ubuntu-latest + steps: + - name: Checkout cpp-linter/.github (org) repo + # needed for .github/labeler.yml config (org-specific) + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + repository: cpp-linter/.github + path: org-repo + - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 + id: labeler + # This step doesn't require a `git checkout`. + # The above checkout step only pulls in the org config. + with: + # If the config file does not exist, then this action will default to + # trying to read the config in project repo + configuration-path: org-repo/.github/labeler.yml + # allow removing labels to keep them in sync with file changes + sync-labels: true + + # The rest of this simply adds labels based on the PR title. + # This isn't really necessary due to how git-cliff parses commits. + # This can be removed once actions/labeler resolves the feature request: + # https://github.com/actions/labeler/issues/809 + - name: Setup Nu shell + # because using bash to manipulate arrays is nonsensical + uses: hustcer/setup-nu@985d59ec83ae3e3418f9d36471cda38b9d8b9879 # v3.20 + with: + version: ${{ vars.NUSHELL_VERSION || '*' }} + - name: Label from PR title + shell: nu {0} + env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.pull_request.number }} + # includes all existing and added labels from previous step + ALL_LABELS: ${{ steps.labeler.outputs.all-labels }} + run: |- + let title = ( + ^gh pr view $env.PR_NUMBER --repo $env.GH_REPO --json "title" -q ".title" + ) + print $"Analyzing title: ($title)" + let labels = $env.ALL_LABELS | split row "," + mut new_labels = [] + if ( + ($title | str contains --ignore-case "fix") + and not ("bug" in $labels) + ) { + $new_labels = $new_labels | append "bug" + } + if ( + ($title | str contains --ignore-case "feat") + and not ("enhancement" in $labels) + ) { + $new_labels = $new_labels | append "enhancement" + } + if ($new_labels | is-not-empty) { + print $"Adding the following labels: ($new_labels | str join ' ')" + gh pr edit $env.PR_NUMBER --add-label ($new_labels | str join ",") + } else { + print "No new labels added based on the PR title" + } diff --git a/.github/workflows/tag-release.nu b/.github/workflows/tag-release.nu new file mode 100644 index 0000000..580aeab --- /dev/null +++ b/.github/workflows/tag-release.nu @@ -0,0 +1,350 @@ +# For details, run: +# nu .github/workflows/tag-release.nu -h + +# Run an external command and output its elapsed time. +# +# Not useful if you need to capture the command's output. +export def --wrapped run-cmd [...cmd: string] { + let app = if ( + ($cmd | first) == "git" + or ($cmd | first) == "gh" + ) { + ($cmd | first 2) | str join " " + } else if ($cmd | first) == 'uvx' { + $cmd | skip 1 | first + } else { + ($cmd | first) + } + print $"(ansi blue)\nRunning(ansi reset) ($cmd | str join ' ')" + let elapsed = timeit {|| ^($cmd | first) ...($cmd | skip 1)} + print $"(ansi magenta)($app) took ($elapsed)(ansi reset)" +} + +# Is this executed in a CI run? +# +# Uses env var CI to determine the resulting boolean +export def is-in-ci [] { + $env | get --optional CI | default 'false' | (($in == 'true') or ($in == true)) +} + +# Is the the default branch currently checked out? +# +# Only accurate if the default branch is named "main". +export def is-on-main [] { + let branch = ( + ^git branch + | lines + | where {$in | str starts-with '*'} + | first + | str trim --left --char '*' + | str trim + ) == 'main' + $branch +} + +# Parse the given `ver` into integer components. +# +# Returns a record. +def "version parse" [ + ver: string, # The fully qualified semver string +] { + ( + $ver + | str trim --left --char "v" + | parse "{major}.{minor}.{patch}" + | first + | into int "major" "minor" "patch" + ) +} + +# Join the given `ver` record's fields into a fully qualified semver string. +def "version join" [ + ver: record, # The record of version components. +] { + $ver | format pattern "v{major}.{minor}.{patch}" +} + +# Get the next version for a new release. +# +# Uses `git-cliff` if `component` is set to `auto`. +export def get-next-version [ + component: string, # The version component. Must be one of [auto, major, minor, patch] + cliff_config?: string, # The path to git-cliff config (cliff.toml) +] { + if ($component == 'auto') { + print "Determining version automatically" + mut args = [--bumped-version] + if ($cliff_config | is-not-empty) { + $args = $args | append [--config $cliff_config] + } + let next = (^uvx git-cliff ...$args) | str trim | version parse $in + print $"Result:" + print $next + version join $next + } else { + let git_out = ( + (^git describe --tags --abbrev=0) + | complete + | ( + if ($in.exit_code != 0) { + "v0.0.0" + } else { + $in.stdout + } + ) + ) + let current_tag = $git_out | str trim | version parse $in + print $"Manually bumping the `($component)` version of:" + print $current_tag + mut new_tag = $current_tag + match $component { + "major" => { + $new_tag.major = $current_tag.major + 1 + $new_tag.minor = 0 + $new_tag.patch = 0 + } + "minor" => { + $new_tag.minor = $current_tag.minor + 1 + $new_tag.patch = 0 + } + "patch" => {$new_tag.patch = $current_tag.patch + 1} + _ => { + error make { + msg: $"'($component)' is not a supported value: [auto, major, minor, patch]" + } + } + } + print "Result:" + print $new_tag + version join $new_tag + } +} + +# Move applicable rolling tags to the checked out HEAD. +# +# For example, `v1` is moved to the newer `v1.2.3` ref. +# +# This does nothing if no tags match the major version +def mv-rolling-tag [ + ver: string # The fully qualified version of the new tag. +] { + let tag = version parse $ver + let major_tag = $"v($tag | get major)" + print $"Moving any tags named '($major_tag)'" + let tags = (^git tag --list) | lines | each {$in | str trim} + if ($major_tag in $tags) { + # delete local tag + run-cmd git tag -d $major_tag + # delete remote tag + run-cmd git push origin $":refs/tags/($major_tag)" + + # create new tag + run-cmd git tag $major_tag + run-cmd git push origin $major_tag + print $"Adjusted tag ($major_tag)" + } +} + +# Find the repo's first commit's SHA. +# +# This is needed for git-cliff to correctly hyperlink the diff +# between the first commit and the first release. +export def find-first-commit [] { + (^git rev-list --max-parents=0 HEAD) | str trim +} + +# Get repo name. +# +# First tries to read from env var `GITHUB_REPO` +# Defaults to parsing the working directory's `origin` remote url. +export def find-repo-name [] { + $env | get --optional "GITHUB_REPO" | default { + let origin = (^git remote get-url origin) | str trim + ( + if (not ($origin | str starts-with "https://")) { + let patched_ssh = $origin | str replace --regex '([^:]):' '$1:/' + $"ssh://($patched_ssh)" + } else { + $origin + } + ) + | url parse + | get path + | str trim --left --char "/" + | path parse + | format pattern "{parent}/{stem}" + } +} + +# Find a cliff.toml to pass to git-cliff +# +# Relies on `GIT_CLIFF_CONFIG` is present in env. +# Otherwise, tries to find it in the following guesses: +# - `../cpp-linter-org/.github/cliff.toml` +# - `../.github/.github/cliff.toml` +export def find-cliff-toml [] { + if ($env | get --optional "GIT_CLIFF_CONFIG" | is-not-empty) { + print $"(ansi green)GIT_CLIFF_CONFIG found in env: ($env.GIT_CLIFF_CONFIG)(ansi reset)" + null + } else { + print $"(ansi yellow)Path to git-cliff config \(cliff.toml) is not set via GIT_CLIFF_CONFIG(ansi reset)" + let parent = "../" | path expand + mut result = null + for guess in [cpp-linter-org .github] { + let hits = glob $"($parent)/($guess)/.github/cliff.toml" + if ($hits | is-not-empty) { + $result = $hits | first + print $"(ansi yellow)Attempting to use ($result) instead.(ansi reset)" + break + } + } + $result + } +} + +const CHANGELOG = "CHANGELOG.md" +export const RELEASE_NOTES = $nu.temp-path | path join "ReleaseNotes.md" + +# Use git-cliff to generate list of changes. +def gen-changes [ + ver: string, # The new version to release + first_commit: string, # The SHA of the first commit for this repo. + gh_repo: string, # The name of the remote repo from which to fetch changes. + cliff_config?: string, # The path to git-cliff config (cliff.toml) + --full, # If given, generate entire CHANGELOG.md. Otherwise, generate just release notes. +] { + mut args = [--tag $ver] + if ($cliff_config | is-not-empty) { + $args = $args | append [--config $cliff_config] + } + if $full { + print $"\nRegenerating the ($CHANGELOG)" + $args = $args | append [--output $CHANGELOG] + } else { + print "\nGenerating Release Notes" + $args = $args | append [ + --output $RELEASE_NOTES --unreleased --strip header + ] + } + + # make an immutable shadow of the mutable variable + let args = $args + with-env {FIRST_COMMIT: $first_commit, GITHUB_REPO: $gh_repo} { + # cannot use mutable variables in this block (a closure) + run-cmd uvx git-cliff ...$args + } +} + +# A script that will +# +# 1. Generate Release notes (from unreleased changes). +# 2. (Re)generate the CHANGELOG.md in repo root and push any changes +# 3. Create a GitHub release, which in turn creates a git tag on the default branch +# 4. Move any major tags (eg. `v2`) that correspond with the new release's tag. +# This step does nothing if no major tag exists. +# +# This script is designed to be run in CI or locally. +# Either way, this script will abort if the current branch is not 'main'. +# +# The following tools are used and must be installed: +# +# - git (https://git-scm.com/) +# - uv (https://docs.astral.sh/uv/) used to install/run git-cliff. +# See .github/requirements.txt for the pinned version of git-cliff. +# - gh-cli (https://cli.github.com/) +# +# Supported `component` parameter values are: +# +# - `auto` (relies on `git-cliff` to determine the next version) +# - `patch` +# - `minor` +# - `major` +# +# If no `component` is given, then `auto` is used. +def main [ + component?: string, # The version component to bump. +] { + let gh_repo = find-repo-name + print $"GH_REPO: ($gh_repo)" + + if ($gh_repo | str ends-with ".github") { + error make { + msg: "The org repo is not intended for deployment; aborting" + } + } + + let first_commit = find-first-commit + print $"First commit is ($first_commit)" + + let is_ci = is-in-ci + + if ($env | get --optional "GITHUB_TOKEN" | is-empty) { + let problem = "No GITHUB_TOKEN set via env var." + let effect = "git-cliff output will not conform to expected behavior" + if $is_ci { + error make { + msg: $"($problem) ($effect)" + } + } else { + print $"(ansi yellow)($problem) (ansi red)($effect)(ansi reset)" + } + } + + # ensure cliff.toml is provided to git-cliff + let cliff_config = find-cliff-toml + + let component = if $component in [auto patch minor major] { + $component + } else { + if ($component | is-not-empty) { + let prompt = $"The component value ($component) is not supported; using `auto` instead." + if $is_ci { + print $"::notice::($prompt)" + } else { + print $prompt + } + } + "auto" + } + let ver = get-next-version $component $cliff_config + + if not (is-on-main) { + error make { + msg: "The default branch is not checked out; aborting" + } + } + + gen-changes $ver $first_commit $gh_repo $cliff_config --full + + # push changes (if any) + run-cmd git add --all + let has_changes = (^git status -s) | lines | is-not-empty + if $has_changes { + if $is_ci { + run-cmd git config --global user.name $"($env.GITHUB_ACTOR)" + run-cmd git config --global user.email $"($env.GITHUB_ACTOR_ID)+($env.GITHUB_ACTOR)@users.noreply.github.com" + } + run-cmd git commit -m $"build: bump version to ($ver)" + run-cmd git push + # HEAD is now moved + } + + gen-changes $ver $first_commit $gh_repo $cliff_config + # push the new tag (pointing at HEAD) by creating a GitHub release + print $"Deploying ($ver)" + run-cmd ...[ + gh + release + create + $ver + --title + $ver + --notes-file + $RELEASE_NOTES + --repo + $gh_repo + ] + + # now move rolling tags if they are present + mv-rolling-tag $ver +} diff --git a/.github/workflows/tag-release.yml b/.github/workflows/tag-release.yml new file mode 100644 index 0000000..f4936cc --- /dev/null +++ b/.github/workflows/tag-release.yml @@ -0,0 +1,89 @@ +name: Tag Release + +on: + workflow_dispatch: + inputs: + project: + description: >- + Which cpp-linter project do you want to deploy? + required: true + type: choice + options: + - cpp-linter-action + - cpp-linter + - clang-tools-pip + - cpp-linter-hooks + - clang-tools-docker + # add more cpp-linter projects here + # NOTE: cpp-linter-rs has its own bump-n-release.yml workflow + component: + description: >- + The version component to bump. + If set to `auto`, then the component will be + determined based on the git commit history since + the previous release. + default: auto + required: true + type: choice + options: + - auto + - patch + - minor + - major + +permissions: {} + +jobs: + create-release: + # NOTE: This job requires a PAT (stored under `secrets.BUMP_N_RELEASE`). + # Using the generic `secrets.GITHUB_TOKEN` to create a release will + # not trigger other CI workflows, namely the release.yml (for deployments). + runs-on: ubuntu-latest + # These permissions must be granted by the token stored in `secrets.BUMP_N_RELEASE` + # permissions: + # # write permission needed to create a GitHub release + # contents: write + # # read permission needed to parse PRs corresponding to commits + # pull-requests: read + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + # credentials needed because `git` is used in tag-release.nu script + persist-credentials: true + repository: cpp-linter/${{ inputs.project }} + path: project-repo + token: ${{ secrets.BUMP_N_RELEASE }} + fetch-depth: 0 + fetch-tags: true + - name: Checkout org repo + # needed for certain config and script files + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + repository: cpp-linter/.github + path: org-repo + - name: Install uv + uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1 + with: + enable-cache: true + cache-dependency-glob: ${{ github.workspace }}/project-repo/uv.lock + - name: Setup Nu shell + uses: hustcer/setup-nu@985d59ec83ae3e3418f9d36471cda38b9d8b9879 # v3.20 + with: + version: ${{ vars.NUSHELL_VERSION || '*' }} + - name: Tag version (${{ inputs.component }}) + env: + UV_CONSTRAINT: ${{ github.workspace }}/org-repo/.github/requirements.txt + # GITHUB_TOKEN used by git-cliff and gh-cli + GITHUB_TOKEN: ${{ secrets.BUMP_N_RELEASE }} + # Some gh-cli commands expect GH_TOKEN instead of GITHUB_TOKEN + GH_TOKEN: ${{ secrets.BUMP_N_RELEASE }} + # for git-cliff exclusively + GITHUB_REPO: cpp-linter/${{ inputs.project }} + GIT_CLIFF_CONFIG: ${{ github.workspace }}/org-repo/.github/cliff.toml + INPUTS_COMPONENT: ${{ inputs.component }} + working-directory: project-repo + run: >- + nu + ${{ github.workspace }}/org-repo/.github/workflows/tag-release.nu + "${INPUTS_COMPONENT}" diff --git a/.github/workflows/unreleased-summary.yml b/.github/workflows/unreleased-summary.yml new file mode 100644 index 0000000..1e8d2bb --- /dev/null +++ b/.github/workflows/unreleased-summary.yml @@ -0,0 +1,44 @@ +on: + workflow_call: + +jobs: + summarize-unreleased: + runs-on: ubuntu-latest + # These permissions must be granted in the calling workflow .yml file + permissions: + contents: read + pull-requests: read + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + fetch-depth: 0 + path: project-repo + - name: Checkout org repo + # needed for certain config and script files + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + repository: cpp-linter/.github + path: org-repo + - name: Install uv + uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1 + with: + enable-cache: true + cache-dependency-glob: ${{ github.workspace }}/project-repo/uv.lock + - name: Run git-cliff + working-directory: project-repo + env: + GITHUB_TOKEN: ${{ github.token }} + GITHUB_REPO: ${{ github.repository }} + GIT_CLIFF_CONFIG: ${{ github.workspace }}/org-repo/.github/cliff.toml + UV_CONSTRAINT: ${{ github.workspace }}/org-repo/.github/requirements.txt + run: >- + uvx + git-cliff + --unreleased + --bump + --output + "${GITHUB_STEP_SUMMARY}" + --strip + header diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..96a4e8b --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "github.vscode-github-actions" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..37441b1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "yaml.schemaStore.enable": false +} diff --git a/cspell.config.yml b/cspell.config.yml index 62d5542..919b82b 100644 --- a/cspell.config.yml +++ b/cspell.config.yml @@ -5,9 +5,13 @@ words: - autobuild - autolabeler - binstall + - endfor + - endmacro - Fatface - htmlcov + - hustcer - mkdocs + - NUSHELL - peaceiris - pipx - pypi @@ -15,6 +19,8 @@ words: - sarif - setuptools - testpypi + - timeit + - topo - venv - xianpengshen - zizmor