diff --git a/scripts/check-deps.nu b/scripts/check-deps.nu new file mode 100644 index 00000000..22640854 --- /dev/null +++ b/scripts/check-deps.nu @@ -0,0 +1,78 @@ +#!/usr/bin/env nu + +use std/log + +# Check release dependencies +export def main [] { + print "Checking Release Dependencies" + print "=============================\n" + + log info "Checking .env file..." + if not (".env" | path exists) { + error make {msg: ".env file not found"} + } + log debug " .env file found" + + log info "Validating Sentry configuration..." + # Get env variables dynamically and reduce the table into a record + let env_vars = ( + open .env + | lines + | where { |line| ($line | str trim) != "" and not ($line | str starts-with "#") } + | each { |line| + let parts = ($line | split column "=" key value | first) + { name: ($parts.key | str trim), value: ($parts.value | str trim) } + } + | reduce --fold {} { |it, acc| $acc | merge { ($it.name): $it.value } } + ) + + let sentry_org = $env_vars | get SENTRY_ORG? + if $sentry_org != "longhorn-developers" { + error make {msg: $"SENTRY_ORG must be set to longhorn-developers in .env\nGot: ($sentry_org)"} + } + log debug $" SENTRY_ORG: ($sentry_org)" + + + let sentry_project = $env_vars | get SENTRY_PROJECT? + if $sentry_project != "ut-registration-plus" { + error make {msg: $"SENTRY_PROJECT must be set to ut-registration-plus in .env\nGot: ($sentry_project)"} + } + log debug $" SENTRY_PROJECT: ($sentry_project)" + + let sentry_auth_token = $env_vars | get SENTRY_AUTH_TOKEN? + if ($sentry_auth_token | is-empty) { + error make {msg: "SENTRY_AUTH_TOKEN must be populated in .env"} + } + log debug " SENTRY_AUTH_TOKEN: [set]" + + #Check for required commands + mut missing_deps = [] + + if (which pnpm | is-empty) { + $missing_deps = ($missing_deps | append "pnpm") + } + if (which npm | is-empty) { + $missing_deps = ($missing_deps | append "npm") + } + if (which conventional-changelog | is-empty) { + $missing_deps = ($missing_deps | append "conventional-changelog") + } + + log info "Checking VCS" + if not (which jj | is-empty) { + log debug " Version control: jujutsu" + } else if not (which git | is-empty) { + log debug " Version control: git" + } else { + $missing_deps = ($missing_deps | append "git or jujutsu") + } + + if ($missing_deps | length) > 0 { + let deps: string = $missing_deps | str join ", " + let error_msg: string = ("Missing required dependencies: " | append $deps | str join) + error make { msg: $error_msg } + } + + log info "All required commands found" + log info "All dependency checks passed" +} diff --git a/scripts/publish-release.nu b/scripts/publish-release.nu new file mode 100644 index 00000000..c1572bb8 --- /dev/null +++ b/scripts/publish-release.nu @@ -0,0 +1,31 @@ +#!/usr/bin/env nu + +use std/log + +# Publish the release (creates distribution package and displays checksum) +export def main [] { + print "Publishing Release" + print "==================\n" + + log info "pnpm zip:to-publish" + let result = (pnpm zip:to-publish | complete) + + if ($result.stderr | str contains -i "error") or ($result.stderr | str contains -i "failed") { + error make {msg: "Package creation failed"} + } + + # Find and verify the zip file + let zip_files = (ls package/*.zip | where type == file) + if ($zip_files | is-empty) { + error make {msg: "No package found in package/ directory"} + } + + # Get last modified zip file + let zip_file = ($zip_files | sort-by -r modified | first | get name) + let checksum = (open $zip_file | hash sha256) + + log info "Release published successfully!" + log info "Package ready for distribution:" + log info $"($zip_file)" + log info $"SHA256: ($checksum)" +} diff --git a/scripts/release.nu b/scripts/release.nu new file mode 100644 index 00000000..2ca45866 --- /dev/null +++ b/scripts/release.nu @@ -0,0 +1,29 @@ +#!/usr/bin/env nu + +use check-deps.nu +use stage-release.nu +use publish-release.nu + +# UTRP Release workflow +export def main [ + --bump: string = "minor", # bump type: major, minor, or patch + --dry-run = true, # Dry run release workflow +] { + check-deps + + if $dry_run { + print "\ndry-run todo:" + print "- stage-release" + print "- publish-release" + exit 0 + } + + mut vcs = "" + if not (which jj | is-empty) { $vcs = "jj" } else if not (which git | is-empty) { $vcs = "git"} + + stage-release $bump $vcs + publish-release + + print "Release workflow completed!" +} + diff --git a/scripts/stage-release.nu b/scripts/stage-release.nu new file mode 100644 index 00000000..63746520 --- /dev/null +++ b/scripts/stage-release.nu @@ -0,0 +1,97 @@ +#!/usr/bin/env nu + +use std/log + +# Stage a new release (bump version, changelog, commit, and tag) +export def main [ + version_type: string = "minor", # Version type: major, minor, or patch + vcs: string = "jj", # VCS: jj or git +] { + print "Staging Release" + print "===============\n" + + if $version_type not-in ["major, minor", "patch"] { + error make {msg: $"version_type must be major, minor, or patch.\nGot: ($version_type)"} + } + + if $vcs not-in ["jj", "git"] { + error make {msg: $"vcs must be jj or git.\nGot: ($vcs)"} + } + + + log info $"Using ($vcs)" + if $vcs == "git" { + # Check for uncommitted changes (git only) + let status = (git diff-index --quiet HEAD -- | complete) + if $status.exit_code != 0 { + error make {msg: "You have uncommitted changes. Please commit or stash them first."} + } + } + + # Bump version in package.json without committing or tagging + log info "Bumping version in package.json..." + npm version $version_type --no-git-tag-version | complete + + let new_version = (open package.json | get version) + log debug $"New version: ($new_version)" + + changelog + + log info "Committing changes..." + if $vcs == "jj" { + jj commit -m $"chore: release v($new_version)" + log debug "Commit created" + + # Update main bookmark (jj only) + log info "Updating main bookmark..." + jj bookmark set main -r @ + log debug "Bookmark 'main' updated to current commit" + } else { + git add package.json package-lock.json CHANGELOG.md + git commit -m $"chore: release v($new_version)" + } + + if $vcs == "jj" { + log info "Creating annotated tag via git..." + jj git export + git tag -a $"v($new_version)" -m $"Release v($new_version)" + jj git import + log debug $"Annotated tag 'v($new_version)' created via git" + } else { + log info "Creating annotated tag..." + git tag -a $"v($new_version)" -m $"Release v($new_version)" + log debug $"Annotated tag 'v($new_version)' created" + } + + log info $"Release v($new_version) staged successfully!" + print "Next steps:" + if $vcs == "jj" { + print " - Review changes: jj show @-" + print " - Push to remote: jj git push && git push --tags" + } else { + print " - Review changes: git show" + print " - Push to remote: git push && git push --tags" + } +} + +def changelog [] { + log info "Generating changelog with new version..." + let changelog_exists = ("CHANGELOG.md" | path exists) + let changelog_before = if $changelog_exists { + (ls CHANGELOG.md | get modified | first) + } + + let result = (pnpm generate-changelog | complete) + if ($result.stderr | str contains -i "error") or ($result.stderr | str contains -i "failed") or ($result.stderr | str contains -i "command not found") { + error make {msg: "Changelog generation failed"} + } + + if not ("CHANGELOG.md" | path exists) { + error make {msg: "CHANGELOG.md was not created"} + } + + let changelog_after = (ls CHANGELOG.md | get modified | first) + if $changelog_exists and ($changelog_after <= $changelog_before) { + error make {msg: "CHANGELOG.md was not updated"} + } +}