Skip to content
This repository has been archived by the owner on Jan 11, 2018. It is now read-only.

Artifacts #164

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES
@@ -1,5 +1,9 @@
= master = master


* Integrity now can expose artifacts created by the builds.
Specify what files should be exposed in project settings.
Shell wildcards are allowed if your file names change from build to build.

* Integrity now has favicons for the home page, project pages and build pages * Integrity now has favicons for the home page, project pages and build pages
reflecting the respective build statuses. reflecting the respective build statuses.


Expand Down
24 changes: 24 additions & 0 deletions lib/integrity/app.rb
@@ -1,3 +1,5 @@
require 'cgi'

module Integrity module Integrity
class App < Sinatra::Base class App < Sinatra::Base
set :root, File.expand_path("../../..", __FILE__) set :root, File.expand_path("../../..", __FILE__)
Expand Down Expand Up @@ -182,6 +184,28 @@ class App < Sinatra::Base
redirect build_url(@build).to_s redirect build_url(@build).to_s
end end


get "/:project/builds/:build/artifacts/:artifact" do |project, build, artifact|
login_required unless current_project.public?

artifact = CGI.unescape(artifact)

artifact_files = current_build.artifact_files
file = artifact_files.detect do |file|
file[:relative_path] == artifact
end

if file.nil?
halt 404
end

fs_path = current_build.build_directory.join(file[:relative_path])
unless File.exist?(fs_path)
halt 404
end

send_file fs_path, :filename => file[:name]
end

get "/:project/builds/:build" do get "/:project/builds/:build" do
login_required unless current_project.public? login_required unless current_project.public?


Expand Down
67 changes: 67 additions & 0 deletions lib/integrity/build.rb
Expand Up @@ -8,6 +8,8 @@ class Build
:pending => "%s hasn't been built yet", :pending => "%s hasn't been built yet",
:building => "%s is building" :building => "%s is building"
} }

GLOB_CHARS = '*?[]'


property :id, Serial property :id, Serial
property :project_id, Integer # TODO :nullable => false property :project_id, Integer # TODO :nullable => false
Expand Down Expand Up @@ -138,5 +140,70 @@ def status
def human_status def human_status
HUMAN_STATUS[status] % sha1_short HUMAN_STATUS[status] % sha1_short
end end

def build_directory
Pathname.new(Integrity.config.directory).join(self.id.to_s)
end

def escape_glob(path)
escaped = path
GLOB_CHARS.each do |char|
escaped = escaped.sub(char, "\\" + char)
end
escaped
end
private :escape_glob

def artifact_files
build_dir = build_directory
=begin
# for file in build dir check below
build_dir = File.expand_path(build_dir)
=end
escaped_build_dir = nil
all_files = []
project.get_artifacts.each do |artifact|
if GLOB_CHARS.split('').any? { |char| artifact.include?(char) }
if escaped_build_dir.nil?
escaped_build_dir = escape_glob(build_dir.to_s)
end

pattern = File.join(escaped_build_dir, artifact)
all_files += Dir[pattern]
else
file = build_dir.join(artifact).to_s
if File.exist?(file)
all_files << file
end
end
end

build_dir_with_slash = build_dir.to_s + '/'

=begin
# check that all files are under the build dir
all_files.map! do |file|
File.expand_path(file)
end
all_files.delete_if do |file|
file[0, build_dir_with_slash.length] != build_dir_with_slash
end
=end

all_files.map! do |file|
if file[0, build_dir_with_slash.length] == build_dir_with_slash
relative_path = file[build_dir_with_slash.length..-1]
else
relative_path = file
end
{
:name => File.basename(file),
:relative_path => relative_path,
}
end
all_files.sort do |a, b|
a[:name] <=> b[:name]
end
end
end end
end end
9 changes: 9 additions & 0 deletions lib/integrity/helpers/resources.rb
Expand Up @@ -16,6 +16,15 @@ def update_notifiers_of(project)
project.update_notifiers(params["enabled_notifiers"] || [], params["notifiers"]) project.update_notifiers(params["enabled_notifiers"] || [], params["notifiers"])
end end
end end

def artifact_files(build)
files = build.artifact_files.dup
files.each do |file|
url = artifact_path(build, file[:relative_path])
file[:url] = url
end
files
end
end end
end end
end end
6 changes: 6 additions & 0 deletions lib/integrity/helpers/urls.rb
@@ -1,3 +1,5 @@
require 'cgi'

module Integrity module Integrity
module Helpers module Helpers
module Urls module Urls
Expand Down Expand Up @@ -60,6 +62,10 @@ def build_url(build, *path)
def build_path(build, *path) def build_path(build, *path)
project_path(build.project, "builds", build.id, *path) project_path(build.project, "builds", build.id, *path)
end end

def artifact_path(build, artifact, *path)
build_path(build, 'artifacts', CGI.escape(CGI.escape(artifact)))
end


# Copyright (MIT) Eric Kidd -- http://github.com/emk/sinatra-url-for # Copyright (MIT) Eric Kidd -- http://github.com/emk/sinatra-url-for
# #
Expand Down
9 changes: 9 additions & 0 deletions lib/integrity/project.rb
Expand Up @@ -11,6 +11,7 @@ class Project
property :uri, URI, :required => true, :length => 255 property :uri, URI, :required => true, :length => 255
property :branch, String, :required => true, :length => 255, :default => "master" property :branch, String, :required => true, :length => 255, :default => "master"
property :command, String, :required => true, :length => 255, :default => "rake" property :command, String, :required => true, :length => 255, :default => "rake"
property :artifacts, String, :required => false, :length => 1000
property :public, Boolean, :default => true property :public, Boolean, :default => true
property :last_build_id, Integer, :required => false property :last_build_id, Integer, :required => false


Expand All @@ -28,6 +29,14 @@ class Project
builds.destroy! builds.destroy!
end end


def get_artifacts
artifacts.split(";")
end

def artifacts_empty?
artifacts.nil? || artifacts.empty?
end

def repo def repo
@repo ||= Repository.new(uri, branch) @repo ||= Repository.new(uri, branch)
end end
Expand Down
2 changes: 2 additions & 0 deletions public/integrity.css
Expand Up @@ -79,6 +79,8 @@ a {
position: relative; } position: relative; }
#content form p.checkbox label { #content form p.checkbox label {
margin-top: 0 !important; } margin-top: 0 !important; }
#content form p.explanation {
margin-left: 8.75em; }
#content form input.text, #content form textarea, #content form select { #content form input.text, #content form textarea, #content form select {
width: 30em; width: 30em;
padding: 0.2em 0.4em; padding: 0.2em 0.4em;
Expand Down
12 changes: 11 additions & 1 deletion views/_build_info.haml
Expand Up @@ -20,10 +20,20 @@
%a{ :href => github_commit_url(build) } view on GitHub %a{ :href => github_commit_url(build) } view on GitHub


- if build.completed? - if build.completed?
- if build.successful? && ! build.project.artifacts_empty?
%h2
Artifacts
%blockquote
%ul#artifacts
- artifact_files(build).each do |file|
%a{ :title => file[:relative_path],
:href => file[:url] } #{file[:name]}
%br/

%h2 %h2
Build Output Build Output
%a{:href => build_path(build, :raw)} (raw) %a{:href => build_path(build, :raw)} (raw)

%pre.output %pre.output
:preserve :preserve
#{bash_color_codes h(build.output)} #{bash_color_codes h(build.output)}
2 changes: 2 additions & 0 deletions views/integrity.sass
Expand Up @@ -106,6 +106,8 @@ a
position: relative position: relative
&.checkbox label &.checkbox label
margin-top: 0 !important margin-top: 0 !important
p.explanation
margin-left: 8.75em
input.text, textarea, select input.text, textarea, select
width: 30em width: 30em
padding: .2em .4em padding: .2em .4em
Expand Down
6 changes: 6 additions & 0 deletions views/new.haml
Expand Up @@ -23,6 +23,12 @@
&== Build script #{errors_on(@project, :command)} &== Build script #{errors_on(@project, :command)}
%textarea#project_build_script{ :name => "project_data[command]", :cols => 40, :rows => 2 } %textarea#project_build_script{ :name => "project_data[command]", :cols => 40, :rows => 2 }
&== #{@project.command.to_s} &== #{@project.command.to_s}

%p.normal{ :class => error_class(@project, :artifacts) }
%label{ :for => "project_artifacts" }<
&== Artifacts to provide
%input.text#project_artifacts{ :name => "project_data[artifacts]", :type => "text", :value => h(@project.artifacts) }
%p.explanation Relative paths of files to expose per build. Separate multiple paths with semicolons. Wildcards are allowed (?, *, []) and must be escaped with a backslash if part of a filename.


%p.normal.checkbox %p.normal.checkbox
%label{ :for => "project_public" } Public project %label{ :for => "project_public" } Public project
Expand Down