server/ci: rework version baking, and cache dist-newstyle in CI

UPDATE: After testing in CI it turns out that the compile time Improvement is better than expected: even though we always have to recompile the OSS lib (due to Version.hs), downstream packages like Pro and multi-tenant can still benefit from some caching and avoid full recompilation.  In the best case this takes us from 22 minutes to 13 minutes total.

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/4104
GitOrigin-RevId: 76cbfc157064b33856e30f4c2b2ab2366f9c6089
This commit is contained in:
Brandon Simmons 2022-04-05 11:57:53 -04:00 committed by hasura-bot
parent dcd3ccb75b
commit fdea752679
5 changed files with 54 additions and 18 deletions

View File

@ -148,6 +148,12 @@ MODE="$1"
PROJECT_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." >/dev/null 2>&1 && pwd )" # ... https://stackoverflow.com/a/246128/176841
cd "$PROJECT_ROOT"
# In CI we use the get version script to actually populate the version number
# that will be compiled into the server. For local development we use this
# magic number, which means we won't recompile unnecessarily. This number also
# gets explicitly ignored in the version test in integration tests.
echo '12345' > "$PROJECT_ROOT/server/CURRENT_VERSION"
# Use pyenv if available to set an appropriate python version that will work with pytests etc.
if command -v pyenv >/dev/null; then
# For now I guess use the greatest python3 >= 3.5

View File

@ -10,6 +10,22 @@ maintainer: vamshi@hasura.io
copyright: Hasura Inc.
category: Database
build-type: Simple
extra-source-files:
-- We use TH to bake in the servers version number at compile time. In order
-- for recompilation detection to work correctly (especially in the presence
-- of caching) we need to both communicate this data via a file (referenced in
-- TH with addDependentFile) /and/ add that file to this section of the cabal
-- file. See: https://github.com/haskell/cabal/issues/4746
--
-- This file is intentionally .gitignore'd
CURRENT_VERSION
-- These are files referenced by functions from 'file-embed' which uses
-- addDependentFile internally and has the same issue as above:
src-rsr/mysql_table_metadata.sql
src-rsr/mssql_table_metadata.sql
src-rsr/catalog_versions.txt
src-rsr/catalog_version.txt
source-repository head
type: git

View File

@ -1,4 +1,4 @@
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TemplateHaskellQuotes #-}
module Hasura.Server.Utils
( APIVersion (..),
@ -16,7 +16,6 @@ module Hasura.Server.Utils
fmapL,
generateFingerprint,
getRequestHeader,
getValFromEnvOrScript,
gzipHeader,
httpExceptToJSON,
isReqUserId,
@ -51,12 +50,10 @@ import Data.ByteString.Base16 qualified as Base16
import Data.ByteString.Lazy qualified as BL
import Data.CaseInsensitive qualified as CI
import Data.Char
import Data.FileEmbed (makeRelativeToProject)
import Data.HashSet qualified as Set
import Data.List.NonEmpty qualified as NE
import Data.Text qualified as T
import Data.Text.Extended
import Data.Text.IO qualified as TI
import Data.Time
import Data.UUID qualified as UUID
import Data.UUID.V4 qualified as UUID
@ -65,13 +62,9 @@ import Database.PG.Query qualified as Q
import Hasura.Base.Instances ()
import Hasura.Prelude
import Language.Haskell.TH.Syntax (Q, TExp)
import Language.Haskell.TH.Syntax qualified as TH
import Network.HTTP.Client qualified as HC
import Network.HTTP.Types qualified as HTTP
import Network.Wreq qualified as Wreq
import System.Environment
import System.Exit
import System.Process
import Text.Regex.TDFA qualified as TDFA
import Text.Regex.TDFA.ReadRegex qualified as TDFA
import Text.Regex.TDFA.TDFA qualified as TDFA
@ -127,13 +120,12 @@ parseStringAsBool t
++ show falseVals
++ ". All values are case insensitive"
-- Get an env var during compile time
getValFromEnvOrScript :: String -> FilePath -> Q (TExp String)
getValFromEnvOrScript var file = do
maybeVal <- TH.runIO $ lookupEnv var
case maybeVal of
Just val -> [||val||]
Nothing -> runScript file
{- NOTE: Something like this is not safe in the presence of caching. The only
way for metaprogramming to depend on some external data and recompile
properly is via addDependentFile and to include that file in the
extra-source-files in the cabal file (see: https://github.com/haskell/cabal/issues/4746).
Leaving this here commented in order to document that fact and also in case
there's a way forward in the future.
-- Run a shell script during compile time
runScript :: FilePath -> Q (TExp String)
@ -151,6 +143,7 @@ runScript file = do
++ " and with error : "
++ stdErr
[||stdOut||]
-}
-- | Quotes a regex using Template Haskell so syntax errors can be reported at compile-time.
quoteRegex :: TDFA.CompOption -> TDFA.ExecOption -> String -> Q (TExp TDFA.Regex)

View File

@ -7,13 +7,16 @@ module Hasura.Server.Version
)
where
import Control.Exception
import Control.Lens ((^.), (^?))
import Data.Aeson (FromJSON (..), ToJSON (..))
import Data.FileEmbed (makeRelativeToProject)
import Data.SemVer qualified as V
import Data.Text qualified as T
import Data.Text.Conversions (FromText (..), ToText (..))
import Hasura.Prelude
import Hasura.Server.Utils (getValFromEnvOrScript)
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import Text.Regex.TDFA ((=~~))
data Version
@ -42,7 +45,21 @@ currentVersion =
fromText $
T.dropWhileEnd (== '\n') $
T.pack $
$$(getValFromEnvOrScript "VERSION" "../scripts/get-version.sh")
-- NOTE: This must work correctly in the presence of a caching! See
-- graphql-engine.cabal (search for “CURRENT_VERSION”) for details
-- about our approach here. We could use embedFile but want a nice
-- error message
$( do
versionFileName <- makeRelativeToProject "CURRENT_VERSION"
addDependentFile versionFileName
let noFileErr =
"\n==========================================================================="
<> "\n>>> DEAR HASURIAN: The way we bake versions into the server has "
<> "\n>>> changed; You'll need to run the following once in your repo to proceed: "
<> "\n>>> $ echo 12345 > \"$(git rev-parse --show-toplevel)/server/CURRENT_VERSION\""
<> "\n===========================================================================\n"
runIO (readFile versionFileName `onException` error noFileErr) >>= stringE
)
-- | A version-based string used to form the CDN URL for fetching console assets.
consoleAssetsVersion :: Text

View File

@ -12,8 +12,12 @@ class TestServerVersion(object):
assert resp.status_code == 200, resp
my_json = resp.json()
assert isinstance(my_json, dict), my_json
# The magic number here means we're compiling for local development and
# this test can be ignored:
if my_json['version'] != '12345':
# The tree may be dirty because we're developing tests locally while
# graphql-engine was built previously when tree was clean. If we're
# modifying graphql-engine too then both of these will be tagged dirty,
# since a rebuild would necessarily be forced:
assert my_json['version'] in (hge_ctx.version, re.sub('-dirty$', '', hge_ctx.version)), my_json
assert my_json['version'] in (hge_ctx.version, re.sub('-dirty$', '', hge_ctx.version)), my_json