Merge branch 'timestamp-restorer' into 'master'
chore: Speed up compilation in CI See merge request mythic-insight/legendary!51
This commit is contained in:
		
						commit
						ef1728815b
					
				
					 5 changed files with 98 additions and 4 deletions
				
			
		| 
						 | 
				
			
			@ -1,5 +1,3 @@
 | 
			
		|||
_build
 | 
			
		||||
deps
 | 
			
		||||
cover
 | 
			
		||||
.elixir_ls
 | 
			
		||||
*.dump
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,10 @@ variables:
 | 
			
		|||
test:
 | 
			
		||||
  stage: test
 | 
			
		||||
  image: "elixir:1.10"
 | 
			
		||||
  cache:
 | 
			
		||||
    paths:
 | 
			
		||||
      - _build/
 | 
			
		||||
      - deps/
 | 
			
		||||
  services:
 | 
			
		||||
  - name: postgres:12
 | 
			
		||||
  script: script/cibuild
 | 
			
		||||
| 
						 | 
				
			
			@ -27,9 +31,14 @@ build_image_for_commit:
 | 
			
		|||
  image: "docker:20.10"
 | 
			
		||||
  only:
 | 
			
		||||
    - master
 | 
			
		||||
  cache:
 | 
			
		||||
    paths:
 | 
			
		||||
      - _build/
 | 
			
		||||
      - deps/
 | 
			
		||||
  services:
 | 
			
		||||
    - name: docker:20.10-dind
 | 
			
		||||
  script:
 | 
			
		||||
    - script/ci/restore-timestamps
 | 
			
		||||
    - docker login "https://${CI_REGISTRY}" -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD
 | 
			
		||||
    - docker pull $CI_REGISTRY_IMAGE:latest || true
 | 
			
		||||
    # This enables fast parallel builds
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +47,9 @@ build_image_for_commit:
 | 
			
		|||
    # Push the commit SHA tagged version to registry. We will later choose to tag that as stable
 | 
			
		||||
    #   if everything passes.
 | 
			
		||||
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
 | 
			
		||||
    # Pull out the built _build/prod directory so we can cache it!
 | 
			
		||||
    - docker cp `docker create $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA`:/root/app/_build/prod _build
 | 
			
		||||
    - script/ci/restore-timestamps
 | 
			
		||||
 | 
			
		||||
# If tests pass, tag the commit and update package versions
 | 
			
		||||
deploy_to_tags:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,16 +26,18 @@ ADD ./apps/admin/mix.exs /root/app/apps/admin/
 | 
			
		|||
ADD ./apps/app/mix.exs /root/app/apps/app/
 | 
			
		||||
ADD ./apps/content/mix.exs /root/app/apps/content/
 | 
			
		||||
ADD ./apps/core/mix.exs /root/app/apps/core/
 | 
			
		||||
ADD ./_build/ /root/app/_build/
 | 
			
		||||
ADD ./deps/ /root/app/deps/
 | 
			
		||||
RUN mix deps.get
 | 
			
		||||
RUN mix deps.compile
 | 
			
		||||
 | 
			
		||||
ADD ./apps /root/app/apps
 | 
			
		||||
 | 
			
		||||
# Leave off here so that we can built assets and compile the elixir app in parallel
 | 
			
		||||
 | 
			
		||||
FROM node:15.0
 | 
			
		||||
 | 
			
		||||
# Build assets in a node container
 | 
			
		||||
ADD ./apps/app/assets/ /root/app/apps/app/assets/
 | 
			
		||||
 | 
			
		||||
WORKDIR /root/app/apps/app/assets/
 | 
			
		||||
COPY --from=0 /root/app/ /root/app/
 | 
			
		||||
RUN npm install
 | 
			
		||||
| 
						 | 
				
			
			@ -43,6 +45,8 @@ RUN npm run deploy
 | 
			
		|||
 | 
			
		||||
FROM elixir1
 | 
			
		||||
 | 
			
		||||
ADD ./apps /root/app/apps
 | 
			
		||||
 | 
			
		||||
# Resume compilation of the elixir app
 | 
			
		||||
ADD ./script /root/app/script
 | 
			
		||||
RUN MAKE=cmake mix compile
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										76
									
								
								script/ci/restore-timestamps
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										76
									
								
								script/ci/restore-timestamps
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,76 @@
 | 
			
		|||
#!/usr/bin/env elixir
 | 
			
		||||
 | 
			
		||||
defmodule TimestampRestorer do
 | 
			
		||||
  @environment System.get_env("MIX_ENV", "dev")
 | 
			
		||||
  @db_path "_build/#{@environment}/timestamp-database"
 | 
			
		||||
 | 
			
		||||
  def sha_all do
 | 
			
		||||
    timestamp_database = load_timestamp_database()
 | 
			
		||||
 | 
			
		||||
    "**/*.{ex,exs,beam}"
 | 
			
		||||
    |> Path.wildcard()
 | 
			
		||||
    |> Enum.reduce(%{}, fn filename, acc ->
 | 
			
		||||
      {sha, timestamp} = process(filename, timestamp_database)
 | 
			
		||||
      Map.put(acc, sha, timestamp)
 | 
			
		||||
    end)
 | 
			
		||||
    |> write_timestamp_database()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp load_timestamp_database() do
 | 
			
		||||
    if File.exists?(@db_path) do
 | 
			
		||||
      @db_path
 | 
			
		||||
      |> File.read!()
 | 
			
		||||
      |> String.split("\n")
 | 
			
		||||
      |> Enum.reduce(%{}, fn line, acc ->
 | 
			
		||||
        [sha, timestamp_string] = String.split(line, ":")
 | 
			
		||||
        {timestamp, ""} = Integer.parse(timestamp_string)
 | 
			
		||||
        Map.put(acc, sha, timestamp)
 | 
			
		||||
      end)
 | 
			
		||||
    else
 | 
			
		||||
      %{}
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp write_timestamp_database(database) do
 | 
			
		||||
    File.mkdir_p!(Path.dirname(@db_path))
 | 
			
		||||
 | 
			
		||||
    database
 | 
			
		||||
    |> Enum.map(fn {key, value} -> "#{key}:#{value}" end)
 | 
			
		||||
    |> Enum.join("\n")
 | 
			
		||||
    |> (& File.write!(@db_path, &1)).()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp process(filename, timestamp_database) do
 | 
			
		||||
    sha = sha(filename)
 | 
			
		||||
    {:ok, %{mtime: new_timestamp}} = File.lstat(filename, time: :posix)
 | 
			
		||||
 | 
			
		||||
    case Map.get(timestamp_database, sha) do
 | 
			
		||||
      nil ->
 | 
			
		||||
        :logger.debug("[NEW SHA  ] #{filename}: #{new_timestamp}")
 | 
			
		||||
      timestamp when timestamp < new_timestamp ->
 | 
			
		||||
        :logger.debug("[RESTORED ] #{filename}: #{timestamp}")
 | 
			
		||||
        File.touch(filename, timestamp)
 | 
			
		||||
      timestamp when timestamp >= new_timestamp ->
 | 
			
		||||
        :logger.debug("[UNCHANGED] #{filename}: #{timestamp}")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    {sha, new_timestamp}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp sha(filename) do
 | 
			
		||||
    hash_ref = :crypto.hash_init(:sha)
 | 
			
		||||
 | 
			
		||||
    File.stream!(filename)
 | 
			
		||||
    |> Enum.reduce(hash_ref, fn chunk, prev_ref->
 | 
			
		||||
      new_ref = :crypto.hash_update(prev_ref, chunk)
 | 
			
		||||
      new_ref
 | 
			
		||||
    end)
 | 
			
		||||
    |> :crypto.hash_final()
 | 
			
		||||
    |> Base.encode16()
 | 
			
		||||
    |> String.downcase()
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
{time, _result} = :timer.tc(TimestampRestorer, :sha_all, [])
 | 
			
		||||
 | 
			
		||||
:logger.info("Restored timestamps in #{time / 1_000_000}s")
 | 
			
		||||
| 
						 | 
				
			
			@ -6,8 +6,12 @@ set -e
 | 
			
		|||
mix local.hex --force
 | 
			
		||||
mix local.rebar --force
 | 
			
		||||
 | 
			
		||||
script/ci/restore-timestamps
 | 
			
		||||
 | 
			
		||||
mix deps.get
 | 
			
		||||
mix ecto.create
 | 
			
		||||
mix ecto.migrate
 | 
			
		||||
 | 
			
		||||
mix test
 | 
			
		||||
 | 
			
		||||
script/ci/restore-timestamps
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue