I have always been fascinated about productivity and how we as developer can make things less manual.
Today I will show my currently most used custom productivity scripts.
Create Branch and PR in draft mode (github)
This script automates the creation of a new branch and a draft pull request, saving a min that would otherwise be spent creating the PR manually.
It takes three parameters:
- TicketNumber (often required to link the branch to a specific ticket)
- Description (combined with the ticket number to form the branch name and used in the PR description)
- BaseBranch (optional, could be enhanced to automatically detect the main branch)
- It uses a PR template if the repository contains one.
- It relies on the GitHub CLI (gh), which uses its own authentication token instead of a personal access token.
- It adds an empty commit because GitHub requires at least one commit to create a pull request.
function Create-BranchAndPR {
param (
[Parameter(Mandatory = $true)]
[string]$TicketNumber,
[Parameter(Mandatory = $true)]
[string]$Description,
[string]$BaseBranch = "master"
)
try {
# Sanitize the description to create a valid branch name
$sanitizedDescription = $Description -replace '[^a-zA-Z0-9\- ]', '-' -replace '[- ]+', '-'
# Construct the branch name
$branchName = "$TicketNumber-$sanitizedDescription"
# Ensure the base branch is up to date
git checkout $BaseBranch
git pull origin $BaseBranch
# Create and switch to the new branch
git checkout -b $branchName
# Create an initial empty commit
git commit --allow-empty -m "Initial empty commit for $branchName"
# Push the new branch to the remote repository
git push -u origin $branchName
# Prepare the PR title
$TicketNumber = $TicketNumber.ToUpper()
$prTitle = "[$TicketNumber] $Description"
# Define the default PR body
$defaultPrBody = @"
# ${prTitle}
ANY ADDITIONAL DESCRIPTION
"@
# Check if a custom PR template exists
$prTemplatePath = ".github/PULL_REQUEST_TEMPLATE.md"
$customPrBody = if (Test-Path $prTemplatePath) {
Get-Content $prTemplatePath -Raw
}
else {
""
}
# Use the custom PR template if it exists and is not empty; otherwise, use the default
$prBody = if (![string]::IsNullOrWhiteSpace($customPrBody)) {
$customPrBody
}
else {
$defaultPrBody
}
# Create the draft pull request using the specified PR body
$prCreateResult = gh pr create `
--title "${prTitle}" `
--body "$prBody" `
--draft `
--head $branchName `
--base $BaseBranch `
2>&1 # Capture error output
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to create pull request: $prCreateResult"
return
}
Write-Host "Draft pull request created successfully!"
}
catch {
Write-Error "An error occurred: $_"
}
}
Get Deploy Status
A quick way to see the deploy status for different repositories in github when using github actions.
Inputs
- Repos – comma-separated repo names (e.g., “api,frontend”).
- Optional switches let you point at another org/user (-Org), pick a different workflow file (-WorkflowName), change the job it looks for (-JobName), and limit how many runs to inspect (-Limit).
Process (per repo)
- Calls the GitHub CLI to pull the latest n runs of the specified workflow.
- For each run (newest first) it fetches the jobs, filters for the job whose name contains $JobName (default “Deploy to prod”), and notes its conclusion.
- As soon as it finds the first successful run, it stops; until then it counts how many runs were incomplete/failed.
Output
- For each run examined, it prints the deploy job’s status, completion time, committer, and run URL, colour-coding success green and everything else red.
- At the end it prints a summary showing how many unsuccessful runs occurred before the most recent successful deploy (or all runs checked if none succeeded).
function Get-DeployStatus {
param(
[Parameter(Mandatory = $true)]
[string] $Repos, # Comma-separated list of repositories
[string] $Org = "your org", # GitHub organisation / owner
[string] $WorkflowName = "cicd", # Workflow name
[string] $JobName = "Deploy to prod", # Job name inside workflow which determine the deploy
[int] $Limit = 10 # How many workflow runs to inspect
)
$RepoList = $Repos -split ',' # Turn list into array
foreach ($Repo in $RepoList) {
Write-Host "Checking repository: $Repo" -ForegroundColor Cyan
Write-Host "---------------------------------------------------"
$workflowRuns = gh run list `
--repo "$Org/$Repo" `
--workflow "$WorkflowName" `
--limit $Limit `
--json "databaseId,headSha,url,conclusion" `
--jq '.[]' 2>$null
if (-not $workflowRuns) {
Write-Host "No workflows named '$WorkflowName' in '$Repo'." -ForegroundColor Red
Write-Host "---------------------------------------------------"
continue
}
$workflowRuns = $workflowRuns | ConvertFrom-Json
$incompleteRuns = 0
$foundSuccess = $false
foreach ($run in $workflowRuns) {
if ($foundSuccess) { break }
$jobs = gh api "/repos/$Org/$Repo/actions/runs/$($run.databaseId)/jobs" `
--jq ".jobs[] | select(.name | contains(\"$JobName\"))" 2>$null |
ConvertFrom-Json
if ($jobs) {
$commit = gh api "/repos/$Org/$Repo/commits/$($run.headSha)" `
--jq '{author: .commit.author.name, date: .commit.author.date}' 2>$null |
ConvertFrom-Json
$status = if ($jobs.conclusion -eq "success") { "Deployed" } else { "Not Deployed" }
$color = if ($jobs.conclusion -eq "success") { "Green" } else { "Red" }
Write-Host " Status : $status" -ForegroundColor $color
Write-Host " Completed At : $($jobs.completed_at -replace 'T',' ' -replace 'Z','')"
Write-Host " Committed By : $($commit.author)"
Write-Host " URL : $($run.url)"
} else {
Write-Host " Status : N/A"
Write-Host " Completed At : N/A"
Write-Host " Committed By : N/A"
Write-Host " URL : $($run.url)"
}
Write-Host "---------------------------------------------------"
if ($run.conclusion -ne "success") {
$incompleteRuns++
} else {
$foundSuccess = $true
}
}
Write-Host ""
Write-Host "============== REPOSITORY SUMMARY ==============" -ForegroundColor Yellow
Write-Host " Repository : $Repo" -ForegroundColor Yellow
Write-Host " Incomplete Runs Until Completed : $incompleteRuns" -ForegroundColor Yellow
Write-Host "==============================================="
Write-Host ""
}
}
Show pull request
A simple script that constructs a URL using the current Git branch and then opens that URL in Chrome (or any other browser).
function Show-PullRequests {
# --------------------------------------------------
# Configurable Variables
# --------------------------------------------------
$organization = "[Enter Organization]" # Used if needed for other org references
$githubOrg = "[Enter github org]" # The GitHub organization or user namespace
$chromePath = "[Enter browser exe]" # For example C:\Program Files\Google\Chrome\Application\chrome.exe
# Use the current directory to determine the project (not strictly required in this script)
$currentDirectory = Get-Location
$directoryName = Split-Path -Leaf $currentDirectory
# Validate the Chrome path
if (-Not (Test-Path $chromePath)) {
Write-Error "Chrome executable not found at path: $chromePath"
exit
}
try {
# Determine the Git root directory
$gitRoot = (& git rev-parse --show-toplevel).Trim()
$repository = Split-Path -Leaf $gitRoot
$project = $repository -split '-' | Select-Object -First 1
# Get the git remote URL
$gitRemote = (& git config --get remote.origin.url).Trim()
if (-Not $gitRemote) {
Write-Error "Could not retrieve git remote URL. Ensure it is configured correctly."
exit
}
# Get the current branch name
$branchName = (git rev-parse --abbrev-ref HEAD).Trim()
if (-Not $branchName -or $branchName -eq "HEAD") {
Write-Error "Could not determine the current branch name. Ensure you are on a branch."
exit
}
# Construct the URL based on the remote type
if ($gitRemote -match "github.com") {
# Handle GitHub URL
$url = "https://github.com/$githubOrg/$repository/compare/$branchName?expand=1"
}
else {
Write-Error "Unsupported git remote URL format: $gitRemote"
exit
}
# Open the URL in Chrome
& $chromePath $url
}
catch {
Write-Error "An error occurred: $_"
exit
}
}
Open solution
This script searches for a solution (.sln) file in the current directory and its subdirectories. It’s handy when you don’t remember the exact name of the solution file and just need to open the single solution in your repository. Although it’s limited if there are multiple .sln files in the folder, in most cases you’ll only have one and won’t need to know its exact name.
function Open-Solution {
[Alias("os")]
Param ([string] $Pattern, [int] $Depth = 2)
if (Test-Path -Path $Pattern -PathType Leaf) {
$ExactMatch = Get-ChildItem $Pattern
Write-Host "Opening $($ExactMatch.Name)..."
Invoke-Expression "& ($ExactMatch.FullName)" | Out-Null
return;
}
$SingleMatch = Get-ChildItem -Recurse -Filter "$Pattern.sln" -Depth $Depth
if ($SingleMatch) {
Write-Host "Opening $($SingleMatch.Name)..."
Invoke-Expression "& ($SingleMatch.FullName)" | Out-Null
return;
}
$MatchingSlnFiles = Get-ChildItem -Recurse -Filter "*$Pattern*.sln" -Depth $Depth
if ($MatchingSlnFiles.Count -eq 0) {
Write-Host "No matching solution found"
return;
}
if ($MatchingSlnFiles.Count -gt 1) {
Write-Host "Multiple matching solutions found:"
$MatchingSlnFiles | ForEach-Object { $_.Name }
return;
}
$matchingSlnFile = $MatchingSlnFiles[0]
Write-Host "Opening $($matchingSlnFile.Name)..."
Invoke-Expression "& $($matchingSlnFile.FullName)" | Out-Null
}
Measure Time
A very simple script which I have use to perform time-measurements when running scripts.
function Measure-Time {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[ScriptBlock]$ScriptBlock
)
# Start the stopwatch
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
try {
# Execute the script block without capturing the output
& $ScriptBlock
}
finally {
# Stop the stopwatch
$stopwatch.Stop()
# Display the execution time
Write-Host "Execution Time: $($stopwatch.Elapsed.TotalSeconds) seconds"
}
}
Usage:
Measure-Time {
# Put the code you want to measure here
Start-Sleep -Seconds 5
}
# Outputs something like: "Execution Time: 5.003 seconds"
Thats pretty much it, happy coding!