diff --git a/cli/source/app.tsx b/cli/source/app.tsx index cc3a7a93f..589a5ca7b 100644 --- a/cli/source/app.tsx +++ b/cli/source/app.tsx @@ -21,6 +21,7 @@ import { useSelector, store, useDispatch } from './store/index.js'; import { getLineLimit } from './utils/line-limit.js'; import { runCommand } from './utils/run-command.js'; import CircularBuffer from './utils/circularBuffer.js'; +import { isLocalBranchBehindRemote } from './utils/update-checker.js'; const CliUi = () => { const dispatch = useDispatch(); @@ -32,6 +33,7 @@ const CliUi = () => { useLogsFromAllSources(); const [retryToggle, setRetryToggle] = useState(false); + const [isUpdateAvailable, setIsUpdateAvailable] = useState(""); const { exit } = useApp(); @@ -90,6 +92,12 @@ const CliUi = () => { }, 200); }, [dispatch, runCommandOpts]); + const handleVersionUpdates = useCallback(async () => { + await isLocalBranchBehindRemote().then((res) => { + setIsUpdateAvailable(res); + }); + }, [setIsUpdateAvailable]); + useEffect(() => { if (appState !== AppStates.TERMINATED) return; exit(); @@ -119,6 +127,10 @@ const CliUi = () => { } }); + useEffect(() => { + handleVersionUpdates(); + }, [handleVersionUpdates]); + useEffect(() => { const { process, promise } = runCommand( 'docker-compose', @@ -347,6 +359,13 @@ const CliUi = () => { {' '} exit + {Boolean(isUpdateAvailable) && ( + <> + + {isUpdateAvailable} + + + )} ); case AppStates.TEARDOWN: diff --git a/cli/source/utils/update-checker.ts b/cli/source/utils/update-checker.ts new file mode 100644 index 000000000..45d55e13a --- /dev/null +++ b/cli/source/utils/update-checker.ts @@ -0,0 +1,42 @@ +import { execSync } from 'child_process'; + +const defaultBranch = 'main'; + +const fetchRemoteRepository = () => { + try { + execSync('git fetch origin'); + } catch (error) { + console.error(`Error fetching the remote repository: ${error}`); + } +}; + +const getLatestLocalCommitSHA = () => { + fetchRemoteRepository(); + const commitSHA = execSync(`git rev-parse ${defaultBranch}`) + .toString() + .trim(); + return commitSHA; +}; + +export async function isLocalBranchBehindRemote(): Promise { + const latestLocalCommitSHA = getLatestLocalCommitSHA(); + + const behindCommits = execSync( + `git rev-list ${latestLocalCommitSHA}..origin/main` + ) + .toString() + .trim() + .split('\n'); + + const behindCommitsCount = behindCommits.filter((commit) => commit).length; + + if (behindCommitsCount == 0) { + return ''; + } + + if (behindCommitsCount == 1) { + return `(1 commit behind remote. pull and rebase)`; + } + + return `(${behindCommitsCount} commits behind remote. pull and rebase)`; +} diff --git a/dev.sh b/dev.sh index 473ad8bff..8cc15f298 100755 --- a/dev.sh +++ b/dev.sh @@ -4,16 +4,42 @@ docker-compose down cat env.example > .env +check_internet_connection() { + curl -s https://www.google.com > /dev/null 2>&1 + if [[ $? -ne 0 ]]; then + echo "No internet connection. Cannot fetch latest commits." + return 1 + fi + return 0 +} + +if ! check_internet_connection; then + BEHIND_COMMITS_COUNT=0 +else + git fetch origin + BEHIND_COMMITS_COUNT=$(git rev-list --count main..origin/main) +fi + GIT_COMMIT_HASH=$(git rev-parse main) GIT_COMMIT_DATE=$(git show -s --format=%cI $MERGE_COMMIT_SHA) -if [[ "$OSTYPE" == "darwin"* ]]; then - sed -i '' "s/^BUILD_DATE=.*/BUILD_DATE=$GIT_COMMIT_DATE/" .env - sed -i '' "s/^MERGE_COMMIT_SHA=.*/MERGE_COMMIT_SHA=$GIT_COMMIT_HASH/" .env -else - sed -i "s/^BUILD_DATE=.*/BUILD_DATE=$GIT_COMMIT_DATE/" .env - sed -i "s/^MERGE_COMMIT_SHA=.*/MERGE_COMMIT_SHA=$GIT_COMMIT_HASH/" .env -fi +update_or_add_env_var() { + local var_name=$1 + local var_value=$2 + if grep -q "^$var_name=" .env; then + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' "s/^$var_name=.*/$var_name=$var_value/" .env + else + sed -i "s/^$var_name=.*/$var_name=$var_value/" .env + fi + else + echo "$var_name=$var_value" >> .env + fi +} + +update_or_add_env_var "BUILD_DATE" "$GIT_COMMIT_DATE" +update_or_add_env_var "MERGE_COMMIT_SHA" "$GIT_COMMIT_HASH" +update_or_add_env_var "BEHIND_COMMITS_COUNT" "$BEHIND_COMMITS_COUNT" cd ./cli || exit diff --git a/web-server/libdefs/ambient.d.ts b/web-server/libdefs/ambient.d.ts index 994fa6caa..a548d8f81 100644 --- a/web-server/libdefs/ambient.d.ts +++ b/web-server/libdefs/ambient.d.ts @@ -84,5 +84,6 @@ declare namespace NodeJS { DB_USER: string; MERGE_COMMIT_SHA: string; BUILD_DATE: string; + BEHIND_COMMITS_COUNT?: string } } diff --git a/web-server/pages/api/internal/version.ts b/web-server/pages/api/internal/version.ts index 12ceab77e..1fbcc91a1 100644 --- a/web-server/pages/api/internal/version.ts +++ b/web-server/pages/api/internal/version.ts @@ -4,6 +4,7 @@ import axios from 'axios'; const dockerRepoName = 'middlewareeng/middleware'; const githubOrgName = 'middlewarehq'; const githubRepoName = 'middleware'; +const defaultBranch = 'main'; const endpoint = new Endpoint(nullSchema); @@ -105,26 +106,26 @@ async function fetchDockerHubTags(): Promise { } async function fetchLatestGitHubCommit(): Promise { - const apiUrl = `https://api.github.com/repos/${githubOrgName}/${githubRepoName}/commits`; - const response = await axios.get(apiUrl); - const latestCommit = response.data[0]; + const apiUrl = `https://api.github.com/repos/${githubOrgName}/${githubRepoName}/commits/${defaultBranch}`; + const response = await axios.get(apiUrl); + const latestCommit = response.data; return latestCommit; } -function isUpdateAvailable( - localVersionInfo: ProjectVersionInfo, - dockerLatestRemoteTag: TagCompressed, - githubLatestCommit: GitHubCommit -): boolean { +function isUpdateAvailable({ + localVersionInfo, + dockerLatestRemoteTag +}: { + localVersionInfo: ProjectVersionInfo; + dockerLatestRemoteTag: TagCompressed; +}): boolean { const env = process.env.NEXT_PUBLIC_APP_ENVIRONMENT; if (env == 'development') { - const localBuildDate = new Date(localVersionInfo.current_build_date); - const latestRemoteCommitDate = new Date( - githubLatestCommit.commit.author.date - ); - - return latestRemoteCommitDate > localBuildDate; + const behindCommitsCount = process.env.BEHIND_COMMITS_COUNT + ? Number(process.env.BEHIND_COMMITS_COUNT) + : 0; + return behindCommitsCount > 0; } const localBuildDate = new Date(localVersionInfo.current_build_date); @@ -156,11 +157,10 @@ async function checkNewImageRelease(): Promise { latest_docker_image: latestDockerImageLink, github_repo: githubRepLink, current_github_commit: versionInfo.merge_commit_sha, - is_update_available: isUpdateAvailable( - versionInfo, - latestTag, - githubLatestCommit - ), + is_update_available: isUpdateAvailable({ + dockerLatestRemoteTag: latestTag, + localVersionInfo: versionInfo + }), latest_docker_image_build_date: latestRemoteDate }; }