2024-03-21 19:30:39 +00:00
#!/usr/bin/env bash
#
# ,::;;,
# ,:;;;:,,;:
# ,::;;+: ,:;;:
# ,;;;:,,++;;:,
# :;;;::++:,
# ++:, ,;:
# ,, ,;: ::+;::: ,,,
# ,:;::;: ::: ,:,;:,*;;;;+,,,,::,,,
# ;: :+: ,;:, ,:++;;:;**;++::::::;;;;:,
# ,; ,::,:;:, :,: :,;;+*+;, :: :;:;;:,
# ,;:;, :;:, ,;:*: ,::?*: :: ,,,, ,:::::;++;;:::::,,
# :; :;:, ,;+; ,:;;; ,::;;;::::::;;:,,,,:+,,,,,:::;;::,
# ;, ,;:, :+, ,:;;, ;::;:,,,:: :: :; ,;?+
# ,;, ,;:, ,;: ,:;:, ,+;, ::, ::,,:;: ,,,::;;;+;
# ,; ,+;, :::,:;++, :;;;;;;;;;;;;;;+;;;::;;;;;::,,:;:,
# :: ,;:,;;, ,;;;:,:+, ,,: ,,:::;;;;;;**;,,,, ,:;:,
# :: ,;:, ,;;, ,;;:, :;;:, ,,::::,,:;:,, ;+*+ ,:;;;,
# ;, ,::, ,:;;;:, ::;,:,,::::, ,:;: ,++:,::;;:,
# ,;, :;, ,:;++, ::+:;:,, :;:, ,:;;;:,
# ,;,;: ,:;++:, ,:;++;, ,;+:, ,:;;;:,
# :; ,,:;+;:, ,:::,,,,,,, :;;+; ,:;;::,
# ;: :;+;:, ,:::, ,;+;;;:::;;,:;;::,
# ;;,;*+:,,:, ,:::, ,;, ,;: :,++:,,
# :;:*;: :;, ,::, ,:, ::, ::;
# ,:+:, :: ,:: ,:, ;: ,;;
# ,;, ,,,: ;;,,,;; ,;; ,:, ,;:,:+;, ,,,,,
# ;::;;;::*;:;;;:::;;++, ,:, ,+;;::;+;;;:::;+,
# +;, :?;;:::, ,+, ,:, :;,,,:;;:, :;
# ,+, :+,,,,,;,,,;; ,:, ;+;;;:;; ,;;
# :; :; ,::;+, ,:,,;:,,, :: ,,:;;,
# ;: :; ;; ,: ++;+;; ,;+;;;::,
# ,;, :: :;: ,: ::,,,::+?*+;:
# :; :: ;::, ,: ,;:,:;;:,;;:*+;
# ,;: :: ,: : ,: :;,;:, :+;+,
# ,;, ;: ;,,: ,:,;+ ,: ,;+:
# ;;;;: ,: :, ,;:*++;;;+++**+:
# ;:,, ;, ;:;+::,, ,:;+:::+;
# ,:,;;:, ,,:;;::::;;:
# +;:,,,:;;;;;;;;::,
# ;?+;;;:,:;;::,
# ,,,+;,,:;
# :;::,
#
#
2024-04-02 18:26:31 +00:00
# /$$$$$$$$ /$$ /$$$$$$$$ /$$$$$$$$ /$$$$$$$$
# | $$_____/| $$ | $$_____/| $$_____/|__ $$__/
# | $$ | $$ | $$ | $$ | $$
# | $$$$$ | $$ | $$$$$ | $$$$$ | $$
# | $$__/ | $$ | $$__/ | $$__/ | $$
# | $$ | $$ | $$ | $$ | $$
# | $$ | $$$$$$$$| $$$$$$$$| $$$$$$$$ | $$
# |__/ |________/|________/|________/ |__/
2024-03-21 19:30:39 +00:00
#
# /$$$$$$$ /$$$$$$$$ /$$ /$$$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$$$ /$$$$$$$
# | $$__ $$| $$_____/| $$ | $$_____/ /$$__ $$ /$$__ $$| $$_____/| $$__ $$
# | $$ \ $$| $$ | $$ | $$ | $$ \ $$| $$ \__/| $$ | $$ \ $$
# | $$$$$$$/| $$$$$ | $$ | $$$$$ | $$$$$$$$| $$$$$$ | $$$$$ | $$$$$$$/
# | $$__ $$| $$__/ | $$ | $$__/ | $$__ $$ \____ $$| $$__/ | $$__ $$
# | $$ \ $$| $$ | $$ | $$ | $$ | $$ /$$ \ $$| $$ | $$ \ $$
# | $$ | $$| $$$$$$$$| $$$$$$$$| $$$$$$$$| $$ | $$| $$$$$$/| $$$$$$$$| $$ | $$
# |__/ |__/|________/|________/|________/|__/ |__/ \______/ |________/|__/ |__/
#
2024-04-05 16:13:58 +00:00
failed = false
2024-03-21 19:30:39 +00:00
usage( ) {
echo " Usage: $0 [options] (optional|start_version) "
echo ""
echo "Options:"
2024-07-01 21:57:11 +00:00
echo " -a, --and_cherry_pick This is a minor release and cherry pick. Used for unscheduled minor releases that are patches + a specific feature"
2024-03-21 19:30:39 +00:00
echo " -c, --cherry_pick_resolved The script has been run, had merge conflicts, and those have been resolved and all cherry picks completed manually."
echo " -d, --dry_run Perform a trial run with no changes made"
echo " -f, --force Skip all confirmations"
echo " -h, --help Display this help message and exit"
2024-04-05 16:13:58 +00:00
echo " -g, --tag Run the tag step"
2025-02-28 21:03:33 +00:00
echo " -k, --skip_dogfood Skip deploying to dogfood. Necessary if you need to release without interrupting demos"
2024-07-01 21:57:11 +00:00
echo " -m, --minor Increment to a minor version instead of patch (required if including non-bugs)"
2024-04-24 18:45:35 +00:00
echo " -n, --announce_only Announce the release only, do not publish the release."
2024-03-21 19:30:39 +00:00
echo " -o, --open_api_key Set the Open API key for calling out to ChatGPT"
echo " -p, --print If the release is already drafted then print out the helpful info"
2024-04-18 19:55:14 +00:00
echo " -q, --quiet This will skip notifying in slack"
2024-03-21 19:30:39 +00:00
echo " -r, --release_notes Update the release notes in the named release on github and exit (requires changelog output from running the script previously)."
echo " -s, --start_version Set the target starting version (can also be the first positional arg) for the release, defaults to latest release on github"
echo " -t, --target_date Set the target date for the release, defaults to today if not provided"
2024-03-27 19:02:35 +00:00
echo " -u, --publish_release Set's release from draft to release, deploys to dogfood."
2024-03-21 19:30:39 +00:00
echo " -v, --target_version Set the target version for the release"
echo ""
echo "Environment Variables:"
2024-04-05 16:13:58 +00:00
echo " OPEN_API_KEY Open API key used for api requests to chat GPT"
2024-04-02 18:26:31 +00:00
echo " SLACK_GENERAL_TOKEN Slack token to publish via curl to #general"
echo " SLACK_HELP_INFRA_TOKEN Slack token to publish via curl to #help-infrastructure"
echo " SLACK_HELP_ENG_TOKEN Slack token to publish via curl to #help-engineering"
2024-03-21 19:30:39 +00:00
echo ""
echo "Examples:"
echo " $0 -d Dry run the script "
echo " $0 -m -v 4.45.1 Set a minor release targeting version 4.45.1 "
echo " $0 --target_version 4.45.1 --open_api_key examplekey "
echo ""
}
2025-02-28 21:03:33 +00:00
# ======================================
# Options
# ======================================
# Initialize variables for the options
minor_cherry_pick = false
cherry_pick_resolved = false
dry_run = false
force = false
minor = false
announce_only = false
open_api_key = ""
start_version = ""
target_date = ""
target_version = ""
print_info = false
publish_release = false
release_notes = false
do_tag = false
quiet = false
skip_deploy_dogfood = false
# Parse long options manually
for arg in " $@ " ; do
shift
case " $arg " in
"--and_cherry_pick" ) set -- " $@ " "-a" ; ;
"--cherry_pick_resolved" ) set -- " $@ " "-c" ; ;
"--dry-run" ) set -- " $@ " "-d" ; ;
"--force" ) set -- " $@ " "-f" ; ;
"--help" ) set -- " $@ " "-h" ; ;
"--minor" ) set -- " $@ " "-m" ; ;
"--announce_only" ) set -- " $@ " "-n" ; ;
"--open_api_key" ) set -- " $@ " "-o" ; ;
"--print" ) set -- " $@ " "-p" ; ;
"--quiet" ) set -- " $@ " "-q" ; ;
"--skip_dogfood" ) set -- " $@ " "-k" ; ;
"--publish_release" ) set -- " $@ " "-u" ; ;
"--release_notes" ) set -- " $@ " "-r" ; ;
"--start_version" ) set -- " $@ " "-s" ; ;
"--tag" ) set -- " $@ " "-g" ; ;
"--target_date" ) set -- " $@ " "-t" ; ;
"--target_version" ) set -- " $@ " "-v" ; ;
*) set -- " $@ " " $arg "
esac
done
# Extract options and their arguments using getopts
while getopts "acdfhgkmno:pqrs:t:uv:w" opt; do
case " $opt " in
a) minor_cherry_pick = true ; ;
c) cherry_pick_resolved = true ; ;
d) dry_run = true ; ;
f) force = true ; ;
h) usage; exit 0 ; ;
g) do_tag = true ; ;
k) skip_deploy_dogfood = true ; ;
m) minor = true ; ;
n) announce_only = true ; ;
o) open_api_key = $OPTARG ; ;
p) print_info = true ; ;
q) quiet = true ; ;
r) release_notes = true ; ;
s) start_version = $OPTARG ; ;
t) target_date = $OPTARG ; ;
u) publish_release = true ; ;
v) target_version = $OPTARG ; ;
?) usage; exit 1 ; ;
esac
done
# ======================================
# Helper Functions
# ======================================
# Shift off the options and optional --
shift $(( OPTIND - 1 ))
2024-03-21 19:30:39 +00:00
# Usage example: Run a command and show spinner for n seconds
# Replace `sleep 5` with your command
# sleep 5 & show_spinner 5
show_spinner( ) {
local pid = $!
local delay = 0.1
local spinstr = '/-\|'
local elapsedTime = 0
local maxTime = $1
printf "Processing "
while [ $elapsedTime -lt $maxTime ] ; do
local temp = ${ spinstr #? }
printf "%c" " $spinstr "
local spinstr = $temp ${ spinstr % " $temp " }
sleep $delay
printf "\b"
elapsedTime = $(( elapsedTime+1))
done
printf "\nDone.\n"
}
check_grep( ) {
# Check if `grep` supports the `-P` option by using it in a no-op search.
# Redirecting stderr to /dev/null to suppress error messages in case `-P` is not supported.
if echo "" | grep -P "" >/dev/null 2>& 1; then
return
else
# Now check if `ggrep` is available.
if command -v ggrep >/dev/null 2>& 1; then
return
else
2024-07-01 21:57:11 +00:00
echo " Please install latest grep with $( brew install grep) "
2024-03-21 19:30:39 +00:00
exit 1
fi
fi
}
2024-04-18 19:55:14 +00:00
check_gh( ) {
gh repo set-default
}
2025-02-28 21:03:33 +00:00
ask( ) {
ask_prompt = $1
if [ " $force " = "false" ] ; then
read -r -p " $ask_prompt " response
case " $response " in
[ yY] [ eE] [ sS] | [ yY] )
echo
; ;
*)
exit 1
; ;
esac
fi
}
2024-03-21 19:30:39 +00:00
check_required_binaries( ) {
local missing_counter = 0
# List of required binaries used in the script
local required_binaries = ( "jq" "gh" "git" "curl" "awk" "sed" "make" "ack" )
for bin in " ${ required_binaries [@] } " ; do
if ! command -v " $bin " & > /dev/null; then
echo " Error: Required binary ' $bin ' is not installed. " >& 2
missing_counter = $(( missing_counter + 1 ))
fi
done
if [ $missing_counter -ne 0 ] ; then
echo " Error: $missing_counter required binary(ies) are missing. Install them before running this script. " >& 2
exit 1
fi
check_grep
2024-04-18 19:55:14 +00:00
check_gh
2024-03-21 19:30:39 +00:00
}
validate_and_format_date( ) {
local input_date = " $1 "
local formatted_date
local correct_format = "%b %d, %Y" # e.g., Jan 01, 2024
# Try to convert input_date to the correct format
formatted_date = $( date -d " $input_date " +" $correct_format " 2>/dev/null)
if [ $? -ne 0 ] ; then
# date conversion failed
echo " Error: Incorrect date format. Expected format example: $correct_format (e.g., Jan 01, 2024) " >& 2
exit 1
else
# Check if the formatted date matches the expected date format
if ! date -d " $formatted_date " +" $correct_format " & >/dev/null; then
# This means the formatted date does not match our correct format
echo " Error: Incorrect date format after conversion. Expected format example: $correct_format (e.g., Jan 01, 2024) " >& 2
exit 1
fi
fi
# If we reached here, the date is valid and correctly formatted
target_date = " $formatted_date " # Update the target_date with the formatted date
echo " Validated and formatted date: $target_date "
}
2025-02-28 21:03:33 +00:00
# Function to determine the best grep variant to use
determine_grep_command( ) {
# Check if `ggrep` is available
if command -v ggrep >/dev/null 2>& 1; then
echo "ggrep" # Use GNU grep if available
elif echo "" | grep -P "" >/dev/null 2>& 1; then
echo "grep" # Use grep if it supports the -P option
else
echo "grep" # Default to grep if ggrep is not available and -P is not supported
# Note: You might want to handle the lack of -P support differently here
fi
}
# Assign the best grep variant to a variable
GREP_CMD = $( determine_grep_command)
# ======================================
# Changelog
# ======================================
2024-04-05 16:13:58 +00:00
build_changelog( ) {
if [ " $dry_run " = "false" ] ; then
make changelog
git diff CHANGELOG.md | $GREP_CMD '^+' | sed 's/^+//g' | $GREP_CMD -v CHANGELOG.md > new_changelog
2025-06-16 15:43:20 +00:00
output = $( cat new_changelog | sed -E ':a;N;$!ba;s/\r{0,1}\n/\\n/g' )
2024-04-05 16:13:58 +00:00
git checkout CHANGELOG.md
if [ [ " $target_date " = = "" ] ] ; then
2024-07-01 21:57:11 +00:00
tartget_date = $( date +"%b %d, %Y" )
2024-04-05 16:13:58 +00:00
fi
echo " ## Fleet $target_milestone ( $tartget_date ) " > temp_changelog
echo "" >> temp_changelog
echo "### Bug fixes" >> temp_changelog
echo "" >> temp_changelog
echo -e " ${ output } " >> temp_changelog
echo "" >> temp_changelog
cp CHANGELOG.md old_changelog
cat temp_changelog
echo
echo "About to write changelog"
2025-02-28 21:03:33 +00:00
ask "Does the above changelog look good (edit temp_changelog now to make changes) (n exits)? [y/N] "
2024-04-05 16:13:58 +00:00
cat temp_changelog > CHANGELOG.md
cat old_changelog >> CHANGELOG.md
rm -f old_changelog
cp CHANGELOG.md /tmp
else
echo "DRYRUN: Would have formatted changelog"
fi
}
changelog_and_versions( ) {
branch_for_changelog = $1
source_branch = $2
2024-07-01 21:57:11 +00:00
local_exists = $( git branch | $GREP_CMD $branch_for_changelog )
2024-04-05 16:13:58 +00:00
if [ " $dry_run " = "false" ] ; then
if [ [ $local_exists != "" ] ] ; then
# Clear previous
git branch -D $branch_for_changelog
fi
git checkout -b $branch_for_changelog
cp /tmp/CHANGELOG.md .
git add CHANGELOG.md
escaped_start_version = $( echo " $start_milestone " | sed 's/\./\\./g' )
2025-02-28 21:03:33 +00:00
version_files = $( ack -l --ignore-dir= tools/release --ignore-dir= articles --ignore-dir= orbit --ignore-dir= server/service --ignore-file= is:CHANGELOG.md " $escaped_start_version " )
2024-04-05 16:13:58 +00:00
unameOut = " $( uname -s) "
case " ${ unameOut } " in
2025-02-28 21:03:33 +00:00
Linux*) echo " $version_files " | xargs sed -i " s/ $escaped_start_version / $target_milestone /g " ;
sed -i -E 's/(version: v[0-9]+\.[0-9]+\.)([0-9]+)/echo "\1$((\2+1))"/e' charts/fleet/Chart.yaml; ;
Darwin*) echo " $version_files " | xargs sed -i '' " s/ $escaped_start_version / $target_milestone /g " ;
sed -i '' -E 's/(version: v[0-9]+\.[0-9]+\.)([0-9]+)/echo "\1$((\2+1))"/e' charts/fleet/Chart.yaml; ;
2024-04-05 16:13:58 +00:00
*) echo "unknown distro to parse version"
esac
git add terraform charts infrastructure tools
2024-05-23 16:07:08 +00:00
git commit -m " Adding changes for Fleet v $target_milestone "
2024-04-05 16:13:58 +00:00
git push origin $branch_for_changelog -f
gh pr create -f -B $source_branch
2024-05-23 16:07:08 +00:00
gh workflow run goreleaser-snapshot-fleet.yaml --ref $source_branch # Manually trigger workflow run
2024-04-05 16:13:58 +00:00
else
2025-09-12 00:46:25 +00:00
echo " DRYRUN: Would have created Changelog / verison pr from $branch_for_changelog to $source_branch "
2024-04-05 16:13:58 +00:00
fi
}
create_qa_issue( ) {
if [ " $dry_run " = "false" ] ; then
# Check for QA issue
found = $( gh issue list --search " Release QA: $target_milestone in:title " --json number | jq length)
if [ [ " $found " = = "0" ] ] ; then
cat .github/ISSUE_TEMPLATE/release-qa.md | awk 'BEGIN {count=0} /^---$/ {count++} count==2 && /^---$/ {getline; count++} count > 2 {print}' > temp_qa_issue_file
gh issue create --title " Release QA: $target_milestone " -F temp_qa_issue_file \
2025-01-17 20:38:39 +00:00
--assignee "pezhub" --label "#g-mdm" --label ":release" \
--assignee "jmwatts" --label "#g-software" \
--assignee "xpkoala" --label "#g-orchestration"
2024-04-05 16:13:58 +00:00
rm -f temp_qa_issue_file
fi
else
echo "DRYRUN: Would have searched for and created if not found QA release ticket"
fi
}
2024-03-21 19:30:39 +00:00
print_announce_info( ) {
2024-04-05 16:13:58 +00:00
if [ " $dry_run " = "false" ] ; then
2024-07-01 21:57:11 +00:00
qa_ticket = $( gh issue list --search " Release QA: $target_milestone in:title " --json url | jq -r .[ 0] .url)
docker_deploy = $( gh run list --workflow goreleaser-snapshot-fleet.yaml --json event,url,headBranch --limit 100 | jq -r " [.[]|select(.headBranch==\" $target_branch \")][0].url " )
2024-04-05 16:13:58 +00:00
echo
echo "For announcing in #help-engineering"
echo "===================================================="
echo " Release $target_milestone QA ticket and docker publish "
echo " QA ticket for Release $target_milestone " $qa_ticket
echo "Docker Deploy status " $docker_deploy
echo " List of tickets pulled into release https://github.com/fleetdm/fleet/milestone/ $target_milestone_number "
echo
slack_hook_url = https://hooks.slack.com/services
app_id = T019PP37ALW
announce_text = " Release $target_milestone QA ticket and docker publish\nQA ticket for Release $target_milestone $qa_ticket \nDocker Deploy status $docker_deploy \nList of tickets pulled into release https://github.com/fleetdm/fleet/milestone/ $target_milestone_number "
2024-04-18 19:55:14 +00:00
if [ " $quiet " = "false" ] ; then
curl -X POST -H 'Content-type: application/json' \
--data " {\"text\":\" $announce_text \"} " \
$slack_hook_url /$app_id /$SLACK_HELP_ENG_TOKEN
fi
2024-04-05 16:13:58 +00:00
else
echo "DRYRUN: Would have printed announce in #help-engineering text w/ qa ticket, deploy to docker link, and milestone issue list link"
fi
2024-03-21 19:30:39 +00:00
}
2024-04-24 18:45:35 +00:00
general_announce_info( ) {
2024-07-01 21:57:11 +00:00
if [ [ " $minor " = = "true" ] ] ; then
2025-02-06 21:48:21 +00:00
article_url = " https://fleetdm.com/releases/fleet- ${ target_milestone //./- } "
2024-07-01 21:57:11 +00:00
article_published = $( curl -is " $article_url " | head -n 1 | awk '{print $2}' )
2024-04-24 18:45:35 +00:00
if [ [ " $article_published " != "200" ] ] ; then
echo " Could't find article at ' $article_url ' "
exit 1
fi
# TODO Publish Linkedin post about release article here and save url
2024-12-19 16:09:22 +00:00
linkedin_post_url = "https://www.linkedin.com/feed/update/urn:li:activity:7274913563989721088"
2024-04-24 18:45:35 +00:00
fi
echo "========================================================================="
echo " Update osquery Slack Fleet channel topic to say the correct version $next_ver "
echo "========================================================================="
# Slack
slack_hook_url = https://hooks.slack.com/services
app_id = T019PP37ALW
announce_text = " :cloud: :rocket: The latest version of Fleet is $target_milestone .\nMore info: https://github.com/fleetdm/fleet/releases/tag/ $next_tag "
2024-07-01 21:57:11 +00:00
if [ [ " $minor " = = "true" ] ] ; then
2024-04-24 18:45:35 +00:00
announce_text = " :cloud: :rocket: The latest version of Fleet is $target_milestone .\nMore info: https://github.com/fleetdm/fleet/releases/tag/ $next_tag \nRelease article: $article_url \nLinkedIn post: $linkedin_post_url "
fi
echo -e $announce_text
if [ " $quiet " = "false" ] ; then
if [ " $dry_run " = "false" ] ; then
curl -X POST -H 'Content-type: application/json' \
--data " {\"text\":\" $announce_text \"} " \
$slack_hook_url /$app_id /$SLACK_GENERAL_TOKEN
curl -X POST -H 'Content -type: application/json' \
--data " {\"text\":\" $announce_text \nDogfood Deployed $dogfood_deploy \"} " \
$slack_hook_url /$app_id /$SLACK_HELP_INFRA_TOKEN
fi
fi
}
2024-03-21 19:30:39 +00:00
update_release_notes( ) {
2024-04-05 16:13:58 +00:00
if [ " $dry_run " = "false" ] ; then
if [ ! -f temp_changelog ] ; then
echo "cannot find changelog to populate release notes"
exit 1
fi
cat temp_changelog | tail -n +3 > release_notes
echo "" >> release_notes
echo "### Upgrading" >> release_notes
echo "" >> release_notes
echo "Please visit our [update guide](https://fleetdm.com/docs/deploying/upgrading-fleet) for upgrade instructions." >> release_notes
echo "" >> release_notes
echo "### Documentation" >> release_notes
echo "" >> release_notes
echo "Documentation for Fleet is available at [fleetdm.com/docs](https://fleetdm.com/docs)." >> release_notes
echo "" >> release_notes
echo "### Binary Checksum" >> release_notes
echo "" >> release_notes
echo "**SHA256**" >> release_notes
echo "" >> release_notes
echo '```' >> release_notes
gh release download $next_tag -p checksums.txt --clobber
cat checksums.txt >> release_notes
echo '```' >> release_notes
echo
echo "============== Release Notes ========================"
cat release_notes
echo "============== Release Notes ========================"
gh release edit --draft -F release_notes $next_tag
else
echo "DRYRUN: Would have created release notes based on temp_changelog"
2024-03-21 19:30:39 +00:00
fi
2024-04-05 16:13:58 +00:00
}
2024-03-21 19:30:39 +00:00
2024-04-05 16:13:58 +00:00
tag( ) {
2024-03-21 19:30:39 +00:00
if [ " $dry_run " = "false" ] ; then
2024-07-01 21:57:11 +00:00
current_branch = $( git rev-parse --abbrev-ref HEAD)
found_version = $( cat CHANGELOG.md | $GREP_CMD $target_milestone )
if [ [ " $found_version " = = "" ] ] ; then
echo "Can't tag if CHANGELOG pr has not been merged yet"
2024-04-05 16:13:58 +00:00
exit 1
fi
2024-07-01 21:57:11 +00:00
if [ [ " $current_branch " != " $target_branch " ] ] ; then
echo " Can't tag release if you aren't on ' $target_branch ' "
exit 1
2024-04-05 16:13:58 +00:00
fi
# Officially tag and push
git tag $next_tag
git push origin $next_tag
# This lets us wait for github actions to trigger
# we are specifically waiting for goreleaser to start
# off the `tag` branch ie: fleet-v4.47.2 to watch until it completes
# The last step of goreleaser is the create the draft release for us to modify later
show_spinner 200
else
echo " DRYRUN: Would have tagged and pushed $next_tag "
fi
if [ " $dry_run " = "false" ] ; then
2024-07-01 21:57:11 +00:00
releaser_out = $( gh run list --workflow goreleaser-fleet.yaml --json databaseId,event,headBranch,url | jq " [.[]|select(.headBranch==\" $next_tag \")][0] " )
echo "Releaser running " $( echo $releaser_out | jq -r ".url" )
2024-04-05 16:13:58 +00:00
2024-07-01 21:57:11 +00:00
gh run watch $( echo $releaser_out | jq -r ".databaseId" )
2024-04-05 16:13:58 +00:00
else
echo "DRYRUN: Would found goreleaser action and waited for it to complete"
2024-03-21 19:30:39 +00:00
fi
2024-04-05 16:13:58 +00:00
# Update draft release notes w/ changelog / notes / checksums
update_release_notes
2024-03-21 19:30:39 +00:00
}
2024-04-05 16:13:58 +00:00
2024-03-27 19:02:35 +00:00
publish( ) {
2024-04-05 16:13:58 +00:00
if [ " $dry_run " = "false" ] ; then
2024-04-24 18:45:35 +00:00
if [ " $announce_only " = "false" ] ; then
# TODO more checks to validate we are ready to publish
gh release edit --draft= false --latest $next_tag
2025-02-28 21:03:33 +00:00
if [ " $skip_deploy_dogfood " = "false" ] ; then
2025-09-12 00:46:25 +00:00
if [ " $force " = "false" ] ; then
read -r -p "Are you ABSOLUTELY SURE you want to deploy to Dogfood right now?? Check for demo's: [y/n]" response
case " $response " in
[ yY] [ eE] [ sS] | [ yY] )
gh workflow run dogfood-deploy.yml -f DOCKER_IMAGE = fleetdm/fleet:$next_ver
show_spinner 200
dogfood_deploy = $( gh run list --workflow= dogfood-deploy.yml --status in_progress -L 1 --json url | jq -r '.[] | .url' )
echo
; ;
*)
dogfood_deploy = "skipped"
echo
; ;
esac
fi
2025-02-28 21:03:33 +00:00
fi
2025-03-11 13:46:01 +00:00
latest_npm = $( npm view fleetctl --json | jq -r '.version' | sed -e 's/^v//' )
latest_local = $( jq -r '.version' tools/fleetctl-npm/package.json | sed -e 's/^v//' )
if [ " $( node -e " console.log(require('compare-versions').compareVersions(' ${ latest_local } ', ' ${ latest_npm } ')) " ) " = "-1" ] ; then
# We're publishing a patch to an older version
cd tools/fleetctl-npm && npm publish "--tag=last-patched-version"
else
# We're publishing the latest version
cd tools/fleetctl-npm && npm publish
fi
2024-04-24 18:45:35 +00:00
2025-09-12 00:46:25 +00:00
issues = $( gh issue list -L 500 -m $target_milestone --json number | jq -r '.[] | .number' )
2024-04-24 18:45:35 +00:00
for iss in $issues ; do
2024-07-01 21:57:11 +00:00
is_story = $( gh issue view $iss --json labels | jq -r '.labels | .[] | .name' | grep story)
2024-04-24 18:45:35 +00:00
# close all non-stories
if [ [ " $is_story " = = "" ] ] ; then
echo " Closing # $iss "
gh issue close $iss
fi
done
echo "Closing milestone"
gh api repos/fleetdm/fleet/milestones/$target_milestone_number -f state = closed
2024-04-18 19:55:14 +00:00
fi
2024-04-05 16:13:58 +00:00
else
echo " DRYRUN: Would have published $next_tag / deployed to dogfood / closed non-stories / closed milestone / announced in slack "
fi
2024-04-24 18:45:35 +00:00
echo "Send general announce"
# Send general announcement in #general
general_announce_info
2024-03-27 19:02:35 +00:00
}
2025-02-28 21:03:33 +00:00
# ======================================
# Validate ok to run
# ======================================
2024-03-21 19:30:39 +00:00
# Validate we have all commands required to perform this script
check_required_binaries
# Now you can use the $dry_run variable to see if the option was set
if $dry_run ; then
echo "Dry run mode enabled."
fi
# Check for OPEN_API_KEY environment variable if no key was provided through command-line options
if [ -z " $open_api_key " ] ; then
if [ -n " $OPEN_API_KEY " ] ; then
open_api_key = $OPEN_API_KEY
else
echo "Error: No open API key provided. Set the key via -o/--open-api-key option or OPEN_API_KEY environment variable." >& 2
exit 1
fi
fi
2024-04-05 16:13:58 +00:00
if [ -z " $SLACK_GENERAL_TOKEN " ] ; then
2024-04-02 18:26:31 +00:00
echo "Error: No SLACK_GENERAL_TOKEN environment variable." >& 2
exit 1
fi
2024-04-05 16:13:58 +00:00
if [ -z " $SLACK_HELP_INFRA_TOKEN " ] ; then
2024-04-02 18:26:31 +00:00
echo "Error: No SLACK_HELP_INFRA_TOKEN environment variable." >& 2
exit 1
fi
2024-04-05 16:13:58 +00:00
if [ -z " $SLACK_HELP_ENG_TOKEN " ] ; then
2024-04-02 18:26:31 +00:00
echo "Error: No SLACK_HELP_ENG_TOKEN environment variable." >& 2
exit 1
fi
2024-03-21 19:30:39 +00:00
if [ [ " $target_date " != "" ] ] ; then
validate_and_format_date $target_date
fi
# ex v4.43.0
if [ -z " $start_version " ] ; then
if [ [ " $1 " = = "" ] ] ; then
# grab latest draft excluding test version 9.99.9
2024-07-01 21:57:11 +00:00
draft = $( gh release list | $GREP_CMD Draft | $GREP_CMD -v 9.99.9)
2024-03-21 19:30:39 +00:00
if [ [ " $draft " != "" ] ] ; then
2024-07-01 21:57:11 +00:00
target_version = $( echo $draft | awk '{print $1}' | cut -d '-' -f2)
start_version = $( gh release list | $GREP_CMD Draft -A1 | tail -n1 | awk '{print $1}' | cut -d '-' -f2)
2024-03-21 19:30:39 +00:00
else
2024-07-01 21:57:11 +00:00
start_version = $( gh release list | $GREP_CMD Latest | awk '{print $1}' | cut -d '-' -f2)
2024-03-21 19:30:39 +00:00
fi
else
start_version = " $1 "
fi
fi
if [ [ $start_version != v* ] ] ; then
2024-07-01 21:57:11 +00:00
start_version = $( echo " v $start_version " )
2024-03-21 19:30:39 +00:00
fi
if [ [ " $target_version " != "" ] ] ; then
if [ [ $target_version != v* ] ] ; then
2024-07-01 21:57:11 +00:00
target_version = $( echo " v $target_version " )
2024-03-21 19:30:39 +00:00
fi
next_ver = $target_version
else
if [ [ " $minor " = = "true" ] ] ; then
next_ver = $( echo $start_version | awk -F. '{print $1"."($2+1)".0"}' )
else
next_ver = $( echo $start_version | awk -F. '{print $1"."$2"."($3+1)}' )
fi
fi
start_ver_tag = fleet-$start_version
2024-07-01 21:57:11 +00:00
if [ [ " $minor " = = "true" ] ] ; then
echo " Minor release from $start_version to $next_ver "
2024-08-01 19:18:59 +00:00
# For scheduled minor releases, we want to branch off of main
start_ver_tag = "main"
2024-04-02 18:26:31 +00:00
else
echo " Patch release from $start_version to $next_ver "
fi
2025-02-28 21:03:33 +00:00
ask "If this is correct confirm yes to continue? [y/N] "
2024-03-27 19:02:35 +00:00
# 4.47.2
2024-03-21 19:30:39 +00:00
start_milestone = " ${ start_version : 1 } "
2024-04-02 20:38:42 +00:00
# 4.48.0
2024-03-21 19:30:39 +00:00
target_milestone = " ${ next_ver : 1 } "
2024-03-27 19:02:35 +00:00
# 79
2024-07-01 21:57:11 +00:00
target_milestone_number = $( gh api repos/:owner/:repo/milestones | jq -r " .[] | select(.title==\" $target_milestone \") | .number " )
2024-04-02 20:38:42 +00:00
# patch-fleet-v4.48.0
2024-10-10 16:36:09 +00:00
target_branch = " rc-patch-fleet- $next_ver "
2024-07-01 21:57:11 +00:00
if [ [ " $minor " = = "true" ] ] ; then
2024-10-10 16:36:09 +00:00
target_branch = " rc-minor-fleet- $next_ver "
2024-04-02 18:26:31 +00:00
fi
2025-02-28 21:03:33 +00:00
update_changelog_prepare_branch = " update-changelog-prepare- $target_milestone "
2024-04-02 18:26:31 +00:00
2024-04-02 20:38:42 +00:00
# fleet-v4.48.0
2024-03-21 19:30:39 +00:00
next_tag = " fleet- $next_ver "
2024-07-01 21:57:11 +00:00
if [ [ " $target_milestone_number " = = "" && " $announce_only " = = "false" && $dry_run = = false ] ] ; then
echo " Missing milestone $target_milestone , Please create one and tie tickets to the milestone to continue "
exit 1
2024-04-05 16:13:58 +00:00
fi
2024-07-01 21:57:11 +00:00
2024-04-05 16:13:58 +00:00
echo " Found milestone $target_milestone with number $target_milestone_number "
2025-02-28 21:03:33 +00:00
# ======================================
# Validation passed check for skip / one-off functions
# ======================================
2024-03-21 19:30:39 +00:00
if [ " $print_info " = "true" ] ; then
2024-06-07 18:04:45 +00:00
if [ " $announce_only " = "false" ] ; then
print_announce_info
exit 0
fi
2024-03-21 19:30:39 +00:00
fi
2024-04-05 16:13:58 +00:00
if [ " $do_tag " = "true" ] ; then
2024-06-07 18:04:45 +00:00
if [ " $announce_only " = "false" ] ; then
tag
exit 0
fi
2024-03-21 19:30:39 +00:00
fi
2024-04-05 16:13:58 +00:00
if [ " $release_notes " = "true" ] ; then
2024-06-07 18:04:45 +00:00
if [ " $announce_only " = "false" ] ; then
update_release_notes
exit 0
fi
2024-03-21 19:30:39 +00:00
fi
2024-03-27 19:02:35 +00:00
if [ " $publish_release " = "true" ] ; then
publish
exit 0
fi
2024-03-21 19:30:39 +00:00
2025-02-28 21:03:33 +00:00
# ======================================
# Start of script unless running after cherry pick step a second time
# ======================================
2024-03-21 19:30:39 +00:00
if [ " $cherry_pick_resolved " = "false" ] ; then
# TODO Fail if not found
if [ " $dry_run " = "false" ] ; then
2024-04-05 16:13:58 +00:00
git fetch
2024-03-21 19:30:39 +00:00
git checkout $start_ver_tag
2024-04-02 18:26:31 +00:00
git pull origin $start_ver_tag
2024-03-21 19:30:39 +00:00
else
2024-08-01 19:18:59 +00:00
echo " DRYRUN: Would have checked out starting at $start_ver_tag "
2024-03-21 19:30:39 +00:00
fi
2024-07-01 21:57:11 +00:00
local_exists = $( git branch | $GREP_CMD $target_branch )
2024-03-21 19:30:39 +00:00
if [ " $dry_run " = "false" ] ; then
if [ [ $local_exists != "" ] ] ; then
# Clear previous
2024-07-01 21:57:11 +00:00
git branch -D $target_branch
2024-03-21 19:30:39 +00:00
fi
2024-07-01 21:57:11 +00:00
git checkout -b $target_branch
2024-03-21 19:30:39 +00:00
else
2024-07-01 21:57:11 +00:00
echo " DRYRUN: Would have cleared / checked out new branch $target_branch "
2024-03-21 19:30:39 +00:00
fi
total_prs = ( )
2025-09-12 00:46:25 +00:00
issue_list = $( gh issue list -L 500 --search 'milestone:"' " $target_milestone " '"' --json number | jq -r '.[] | .number' )
2024-07-01 21:57:11 +00:00
if [ [ " $issue_list " = = "" && " $dry_run " = = "false" ] ] ; then
2024-03-21 19:30:39 +00:00
echo " Milestone $target_milestone has no target issues, please tie tickets to the milestone to continue "
exit 1
fi
2024-07-01 21:57:11 +00:00
2024-03-21 19:30:39 +00:00
echo " Issue list for new patch $next_ver "
echo $issue_list
2024-07-01 21:57:11 +00:00
2024-03-21 19:30:39 +00:00
for issue in $issue_list ; do
2024-07-01 21:57:11 +00:00
prs_for_issue = $( gh api repos/fleetdm/fleet/issues/$issue /timeline --paginate | jq -r '.[]' | $GREP_CMD "fleetdm/fleet/" | $GREP_CMD -oP "pulls\/\K(?:\d+)" )
2024-03-21 19:30:39 +00:00
echo -n " https://github.com/fleetdm/fleet/issues/ $issue "
if [ [ " $prs_for_issue " = = "" ] ] ; then
2025-05-22 16:47:40 +00:00
echo -n " - No PRs found."
2024-03-21 19:30:39 +00:00
fi
for val in $prs_for_issue ; do
echo -n " $val "
total_prs += ( " $val " )
done
echo
done
2025-02-28 21:03:33 +00:00
ask "Check any issues that have no pull requests, no to cancel and yes to continue? [y/N] "
2024-03-21 19:30:39 +00:00
commits = ""
2024-07-01 21:57:11 +00:00
if [ [ " $minor " = = "false" || " $minor_cherry_pick " = = "true" ] ] ; then
2024-04-05 16:13:58 +00:00
echo "Continuing to cherry-pick"
2024-04-02 18:26:31 +00:00
for pr in ${ total_prs [*] } ;
do
2024-07-01 21:57:11 +00:00
output = $( gh pr view $pr --json state,mergeCommit,baseRefName)
state = $( echo $output | jq -r .state)
commit = $( echo $output | jq -r .mergeCommit.oid)
2025-02-28 21:03:33 +00:00
target_pr_branch = $( echo $output | jq -r .baseRefName)
echo -n " $pr $state $commit $target_pr_branch : "
if [ [ " $state " != "MERGED" || " $target_pr_branch " != "main" ] ] ; then
2024-04-02 18:26:31 +00:00
echo " WARNING - Skipping pr https://github.com/fleetdm/fleet/pull/ $pr "
2024-03-21 19:30:39 +00:00
else
2024-04-02 18:26:31 +00:00
if [ [ " $commit " != "" && " $commit " != "null" ] ] ; then
echo " Commit looks valid - $commit , adding to cherry-pick "
commits += " $commit "
else
echo " WARNING - invalid commit for pr https://github.com/fleetdm/fleet/pull/ $pr - $commit "
fi
2024-03-21 19:30:39 +00:00
fi
2024-04-02 18:26:31 +00:00
#echo "======================================="
done
2024-03-21 19:30:39 +00:00
2024-04-02 18:26:31 +00:00
for commit in $commits ;
do
# echo $commit
2024-07-01 21:57:11 +00:00
timestamp = $( git log -n 1 --pretty= format:%at $commit )
2024-04-02 18:26:31 +00:00
if [ $? -ne 0 ] ; then
echo " Failed to identify $commit , exiting "
exit 1
fi
# echo $timestamp
time_map[ $timestamp ] = $commit
done
2024-03-21 19:30:39 +00:00
2024-04-02 18:26:31 +00:00
timestamps = ""
for key in " ${ !time_map[@] } " ; do
timestamps += " $key \n "
done
2024-07-01 21:57:11 +00:00
for ts in $( echo -e $timestamps | sort) ; do
2024-04-02 18:26:31 +00:00
commit_hash = " ${ time_map [ $ts ] } "
# echo "# $ts $commit_hash"
if git branch --contains " $commit_hash " | $GREP_CMD -q " $( git rev-parse --abbrev-ref HEAD) " ; then
echo " # Commit $commit_hash is on the current branch. "
is_on_current_branch = true
else
# echo "# Commit $commit_hash is not on the current branch."
if [ [ " $failed " = = "false" ] ] ; then
if [ " $dry_run " = "false" ] ; then
git cherry-pick $commit_hash
if [ $? -ne 0 ] ; then
echo " Cherry pick of $commit_hash failed. Please resolve then continue the cherry-picks manually "
failed = true
fi
else
echo " DRYRUN: Would have cherry picked $commit_hash "
2024-03-21 19:30:39 +00:00
fi
else
2024-04-02 18:26:31 +00:00
echo " git cherry-pick $commit_hash "
2024-03-21 19:30:39 +00:00
fi
2024-04-02 18:26:31 +00:00
is_on_current_branch = false
2024-03-21 19:30:39 +00:00
fi
2024-04-02 18:26:31 +00:00
done
fi
2024-03-21 19:30:39 +00:00
fi
2025-02-28 21:03:33 +00:00
# ======================================
# Automatic cherry pick succeeded or cherry picked manually; continue
# ======================================
2024-03-21 19:30:39 +00:00
if [ [ " $failed " = = "false" ] ] ; then
if [ " $dry_run " = "false" ] ; then
2024-04-05 16:13:58 +00:00
# have to push so we can make the PR's back
2025-02-28 21:03:33 +00:00
git push origin $target_branch
ask "Did git push work? [y/n]"
2024-04-05 16:13:58 +00:00
fi
2024-03-21 19:30:39 +00:00
2025-02-28 21:03:33 +00:00
2024-04-05 16:13:58 +00:00
build_changelog
2024-03-21 19:30:39 +00:00
2024-07-01 21:57:11 +00:00
# Create PR for changelog and version to release
changelog_and_versions $update_changelog_prepare_branch $target_branch
2024-03-21 19:30:39 +00:00
2025-02-28 21:03:33 +00:00
ask "Did first changelog work? [y/n]"
2024-03-21 19:30:39 +00:00
if [ " $dry_run " = "false" ] ; then
2024-04-05 16:13:58 +00:00
# Create PR for changelog and version to main
2024-03-21 19:30:39 +00:00
git checkout main
git pull origin main
2025-02-28 21:03:33 +00:00
ask "Are you on main? [y/n]"
2025-09-12 00:46:25 +00:00
changelog_and_versions " $update_changelog_prepare_branch " -main main
git checkout $target_branch
2024-03-21 19:30:39 +00:00
else
2024-04-05 16:13:58 +00:00
echo "DRYRUN: Would have switched to main and pulled latest"
2024-03-21 19:30:39 +00:00
fi
2025-02-28 21:03:33 +00:00
2024-04-05 16:13:58 +00:00
# Check for QA issue
create_qa_issue
2024-03-21 19:30:39 +00:00
if [ " $dry_run " = "false" ] ; then
2024-04-05 16:13:58 +00:00
echo "Waiting for github actions to propogate..."
2024-03-21 19:30:39 +00:00
show_spinner 200
fi
2024-04-05 16:13:58 +00:00
# For announce in #help-engineering
print_announce_info
2024-03-21 19:30:39 +00:00
else
# TODO echo what to do
echo "Placeholder, Cherry pick failed....figure out what to do..."
exit 1
fi