We haven't used this in many years (it was really only used for nix and patchelf releases).
N74SRHS6XG3M4II7HDCJZKFF6RFXEVGBUN3DN4FUEMELHYCGMHLQC
ZQS3ZNA33VLIZJV273K2PVSAL2BDGUVAXEQ43Q4BU3GMXD4LTFGAC
SLBM7H4QGAX5J25YXYZL7APK5Y3TSB4KSWCEJPF3X3KF7UG7Y3GQC
6S3ZLOZL2QXJ2PUDVFFGO4PXXNLFGVNVFQXC6CLPP3UH6WGWDFXQC
GRMV5GEWXL5HRCQ5AST22ATEPO3VNF4TSJWLYT3J5GPCM2CGRK4AC
ICRGXEPIPD7RFHFMBKWD7ZOAUHARG47VQH3WK3HJFYCV5ZVBQGHQC
75BCCX2TABRXQFYV73D6QU3VCT7LZUZEOWUK4X5USPWWTBTVJTLQC
CUFVKLLAL54OHDMYUHVJZK46LLX7RJMUJUE5HWYDCGIOQ5TIGXZQC
UUGBVEGYV3FUNL7D3ECA2DIMFHE2S5UQF4ACSLESX3M3NRYYE57AC
D5QIOJGPKQJIYBUCSC3MFJ3TXLPNZ2XMI37GXMFRVRFWWR2VMTFAC
KXGOUX7PH4BOXPJRYRGDS4RY7RTXYWUXCRAB5JR3BLVS62PVKOQAC
AS2OXLRMJGRI64FIEM4T7EV24NZYIMPPR2EQN3SR5A2JBHVXNYXAC
SU566LI3TBPIEOOXKN3HWVUMGXCCHV3AX7QTYHCAMA5QGYAKUBAQC
3PNG7NIBQQURUUPRVQXYL342OT7JUUYOMY2JJNP6YDX7SYJDZMYAC
JARRBLZDQ2JZWY7IUVPTOT7WJMBPMLFLF2MGLVGOYROAAISYGLSAC
RJICSUYGE5RVDXYAPCOTS436V67HB222GG6GAWEATJEQAAKDU5SAC
GPHLV42M5EGNMSMFVZ54H3LY6QD5R4FE43565A7HJMI3V23FPDCAC
LZVO64YG43JD7YMZSCTZNOBS5ROZA4FMPKJW2YOMHX2V5PTGBVWQC
OR5SJ42Y6HYDJL3UTEXMINLNY2T3LYSVJ6FSYFMHP4K4W2RZAMIQC
AWMM5OGV5EM4ZA3T3SCCU6LMKVJHKI2JANCNA236MD3O7AELE5XAC
WE5Q2NVIIK4R2DUUZYLJFQVYK5O26EDEJRPK3CPGWHU3SEAC2DQAC
LBNVQXUBEZ45SOTGVXK5UEZXIAIZTJLWZNUYFI4JZ6J65N3KPDVQC
Z6MDQIGODVE7RXX5U5D64EEC2ZDEZ36Z4ZMPFYIHWBG42IXUDYFQC
GJFYEU3SVP7TDSYXVZEYGKN4NVWSZX4754PPPTOYPRHUO5RMDWPQC
PMNWRTGJ4GVSMSSAWSUD57B26PCRAHMZIQ5SIWJIK7A74ENKEQLAC
KRVD4EW7JPDBJJKOD7TM2PATKNKOFMEJESWP5RCISNAI2ZNK424QC
UOINKJ2JBCRTZPFTAXDPRJOEUWWPJ43IGVWLRIRLIGHLBMWDOGDQC
WRIU3S5EO3RB3IM5PUDNHLOOMUPD5UKWNUL4YMAKD3C6O4KELJCAC
KQZQI2WNMXADNASI7OOMB56OOSBAARTI5ZWY52TUVHHJW6D2HMVAC
U4TD3AIQXBJFFUORTMIC4IHZTVBORRKL2TZ2FSP4G665ECZOEMNAC
K63JYJDHWZF2STHFTA2GLRQAGMHMATYBSLLGLJS7BTOP6YZPREDAC
6HWIUOSURE7QRA7AUY2Y6CSNWYV36CAXCZ6I6QU24U7GBZ3ODD5AC
BKOIYITRBRVU4D7XFAZPV5QHAPCBMIO3SBNHYJ6TVT43WR32CTHQC
FPK5LF53CFUEKFYJ3IYXT4UTVC6IITWJOCFATMC4PLHEUP5SIEAAC
RBNQKATLSAKTGW2IRNB5CRV3SEH5F6E4BPVWX4BII7MH5TCIPINQC
SB2V735VJ2CDHGCXRUA5FOYHDRXQFVOZ3KXC3YKXWRNW6DIX7RXQC
3HCBU2FAXZMSF4JJR5Q64BSN66MBOGVETNHK33V2WSNDGOF4HHQAC
JM3DPYOMVNMCL5GMEYC3Y4NDRGTNIFBBFTPGPVT66GPENVPU7EVQC
IGR322YPZG7IX33I6CSF4ZIHFKXCT4ERC2LC73MW6PN7W53D73BAC
Y6AHH4THYQA43V77L43YM42DYRPCMDSWLUV4NKWAQYMPL4NTUIPQC
JTRG7RDQXKPSO4ESGDLSVAT5WIFGKDL424MN6YYCVTKCOR2FTXRQC
J5UVLXOK6EDIL5I7VKWH4V2QDS4DPD7FHRK6XBWSXFRQS4JKXFZQC
CLJQCY2XHIDNNMFBJ5PK3GQEN6RFALEFKXBJRWZPEIKR4PR5ZQ5AC
PHX2HIVGHHKCAX6VNN2WXD4LRGSA74KQMJCCTMHK7HS6JPELVECAC
RFE6T5LGBFFNEPHZOPF4UNMFC2L4CGD5TPAMOXDLRPH3TZJ43UBAC
FTPCV25MOLQUNR5CAR453W7T7QTUZRLPLEOSDZ5HSDFAXQZVHOYQC
L2E6EVE2RVFVDCUNRJ4CZYSQNS2DZUA5DTBETHBDUQUV2KQQRAOQC
ECBA3GQOGTF73Y7A5EFUXZ5PDIZ5NPJM3WMOUJTE3AEK2PZQX3MQC
FHF6IZJQPUQHY5QWQYRPZVDBRLHREWRHGNKVQDT7F3GQKKLZXJKQC
X27GNHDV5KPZ5GSH6DCAJMNCEMZLCP7M43JWF2X3O5QWXMOX273AC
P5X4P6VKS5CJOOLJRVL66GRJLDLVC3EKAVAHP2RJOXQJ7WTYAUBQC
KSBB33RE2PK5SFN7ZMOTZJQHZB4JYIIUUKWDSD3LSZ5GD465AJHQC
7ECJWNVXNO3BKM7B7FIFIRBE77QET5PK2C3XKVQUXCYKHDP3V4UQC
PZL3SZM3U3BYJX2RGYXC6NMBG7WQHFWHSYDYXZ7Q5VZA3EDYVPIQC
OW6XV2YSTYTBR5JO4FKCYAH44WX5UI2HUPIM6UIEQF7SPV7RLQFQC
2G63HKCHG7S6DGWDOHSDF7PXFPD6H4TRKDKIIFCXXAKET6FCWN2AC
JIJDYWPYMZZNFBCWYSYR72RNEW5MSI26MJ5UXXIU7DDX25IBR3CQC
JLICHVE674VIYYLAKDWYO7RZ7YREVPDYR3DRRJ57DXR2OVHP34CQC
2R7GHSA4NUXPRWRPVXYDYWFIVVJGSUWUI2IPJGFN6ILZIAZGXGCAC
2GUAKGTBTNFFER343SQWSLFYIXXHJLDSGH5JHF7QMC3AVZB7Q3TQC
ZI535LI6PJMKSOBJE33B3RRZ5S2JVTR3XPUDTSXJW6BZNTAHS3GQC
ASIRNHAHLLITDIL4T6OTHA3AERVAO2GP3SFFPLZGT63NGDW3ZLNQC
XTKCALUAJ2EKYO6ZQDLRDCDLCM5NAMUJ5BB3N6XIY32Q6YOJTB5QC
6WRGCITDYP7JIBYP25QIWCHWRJWFPDP2D3TJS3WO3KUHQQJAWHMQC
77BG3TYKSKVV6C6VDJH7JDRJRPRNNBAO2OH4YZ6JY5Q5OP3VGXFQC
7YBYT2LQML2PKEO6UO4444AGSASS664UCDXW2YO3ALB7THQHCEBQC
225GEK4NN53NJ6EBDAJNECR76FILAHV7KNEG23TDQ2KYMG7NFEUAC
JY7BXXOP3EZCDT5RSMVE4Y6IECXGYL4GEBJOZHR7H3Z35XZ3NIVQC
W6DC6K4INJQOJYR553ISCKZV7YIOGHEM3FZQPOLAPSZQ3KSJDMRQC
2VBQRQ2QUNM3ZC7I7WAXYSUVPP52IY2KLE4CVS3E4MAHL2T42KKQC
ODNCGFQ5FPKFI624BVMLW7PJ2EFJOR3TY66OCZM42UNNTWBCF2TQC
VG4QG336SCZNWAXJERI3N5FO6PUAYFJV24CLI27ADUFPO74RVJKQC
BIVZGPUTQ2C7X6NJQMVIDDO2OYNO4R3GROGQAWBVNSK2HSZ3REOQC
ACFFJUAN5R2AFJVVJ5OBX6BQHT3M4SHO7QXILF5LURVIFPDORLIQC
KJQWSRCCQEKF64L4NYYZ7VKAF2YEPYXXTRW6BI464P6Q6KU34TQAC
WZ3AEJ67LOG5L335AAC2BDLIJPIU4VSCGBMATBHDZC26ECRS5A6AC
SZPBGW2NVHOOW4E3VIB2B35V7YRF67DFVVGUK573LVD574IHSA6AC
6BLUKEQ2M5RGWMPXPYIFIEVEUBV4PYAZ75S2WSBIATMRGYFMQZHQC
N22GPKYTOLZLBGTGDATQDVZ4R5APZEAOIA7L32X4UXBH4XNI7MWAC
T2232OBSNVDGHY46RY5BBB2IET5LV5OLKSSBDYUF7KAEDBUOTC7QC
5SHCWE7XPQORSOLY7HGAIK2ODKBFPY4KVXRL2W7X6D4WALU544HQC
E2TOU3L66CH5DA4XPATQM5YM63SFXX63V7SDOIGS4ND3GR7HQALAC
34DPX2ORV3XDB7M3NYVZLU6MYDIFCIC5NHPJW7N5ITS356GWVMCAC
4S5JF5JPKWTDYHFJMTXOFTDAMYHD5ON2UBTLMGVYPJCP6QYIM2EAC
* Creating a release set:
insert into ReleaseSets(project, name) values('patchelf', 'unstable');
insert into ReleaseSetJobs(isPrimary, project, release, job, attrs, description) values(1, 'patchelf', 'unstable', 'tarball', 'officialRelease=false', 'Source distribution');
insert into ReleaseSetJobs(project, release, job, attrs, description) values('patchelf', 'unstable', 'build', 'system=i686-linux', 'Build on i686-linux');
insert into ReleaseSetJobs(project, release, job, attrs, description) values('patchelf', 'unstable', 'build', 'system=x86_64-linux', 'Build on x86_64-linux');
insert into ReleaseSetJobs(project, release, job, attrs, description, mayFail) values('patchelf', 'unstable', 'rpm_fedora9i386', '', 'Fedora 9 (i386)', 1);
insert into ReleaseSetJobs(project, release, job, attrs, description) values('patchelf', 'unstable', 'rpm_fedora10i386', '', 'Fedora 10 (i386)');
insert into ReleaseSetJobs(project, release, job, attrs, description) values('patchelf', 'unstable', 'deb_ubuntu804i386', '', 'Ubuntu 8.04 (i386)');
insert into ReleaseSets(project, name) values('patchelf', 'stable');
insert into ReleaseSetJobs(isPrimary, project, release, job, attrs, description) values(1, 'patchelf', 'stable', 'tarball', 'officialRelease=true', 'Source distribution');
insert into ReleaseSetJobs(project, release, job, attrs, description) values('patchelf', 'stable', 'build', 'system=i686-linux', 'Build on i686-linux');
insert into ReleaseSetJobs(project, release, job, attrs, description) values('patchelf', 'stable', 'build', 'system=x86_64-linux', 'Build on x86_64-linux');
insert into ReleaseSetJobs(project, release, job, attrs, description, mayFail) values('patchelf', 'stable', 'rpm_fedora9i386', '', 'Fedora 9 (i386)', 1);
insert into ReleaseSetJobs(project, release, job, attrs, description) values('patchelf', 'stable', 'rpm_fedora10i386', '', 'Fedora 10 (i386)');
insert into ReleaseSetJobs(project, release, job, attrs, description) values('patchelf', 'stable', 'deb_ubuntu804i386', '', 'Ubuntu 8.04 (i386)');
package Hydra::Controller::Release;
use strict;
use warnings;
use base 'Catalyst::Controller';
use Hydra::Helper::Nix;
use Hydra::Helper::CatalystUtils;
sub release : Chained('/') PathPart('release') CaptureArgs(2) {
my ($self, $c, $projectName, $releaseName) = @_;
$c->stash->{project} = $c->model('DB::Projects')->find($projectName)
or notFound($c, "Project $projectName doesn't exist.");
$c->stash->{release} = $c->stash->{project}->releases->find({name => $releaseName})
or notFound($c, "Release $releaseName doesn't exist.");
}
sub view : Chained('release') PathPart('') Args(0) {
my ($self, $c) = @_;
$c->stash->{template} = 'release.tt';
}
sub updateRelease {
my ($c, $release) = @_;
my $releaseName = trim $c->request->params->{name};
error($c, "Invalid release name: $releaseName")
unless $releaseName =~ /^$relNameRE$/;
$release->update(
{ name => $releaseName
, description => trim $c->request->params->{description}
});
}
1;
sub submit : Chained('release') PathPart('submit') Args(0) {
my ($self, $c) = @_;
requireProjectOwner($c, $c->stash->{project});
if (($c->request->params->{action} || "") eq "delete") {
$c->model('DB')->schema->txn_do(sub {
$c->stash->{release}->delete;
});
$c->res->redirect($c->uri_for($c->controller('Project')->action_for('project'),
[$c->stash->{project}->name]));
} else {
$c->model('DB')->schema->txn_do(sub {
updateRelease($c, $c->stash->{release});
});
$c->res->redirect($c->uri_for($self->action_for("view"),
[$c->stash->{project}->name, $c->stash->{release}->name]));
}
}
}
sub edit : Chained('release') PathPart('edit') Args(0) {
my ($self, $c) = @_;
requireProjectOwner($c, $c->stash->{project});
$c->stash->{template} = 'edit-release.tt';
$c->stash->{members} = [$c->stash->{release}->releasemembers->search({},
{order_by => ["description"]})];
$release->releasemembers->delete;
foreach my $param (keys %{$c->request->params}) {
next unless $param =~ /^member-(\d+)-description$/;
my $buildId = $1;
my $description = trim $c->request->params->{"member-$buildId-description"};
$release->releasemembers->create({ build => $buildId, description => $description });
}
$c->stash->{members} = [$c->stash->{release}->releasemembers->search({},
{order_by => ["description"]})];
my $releaseName = trim $c->request->params->{name};
my $release = $build->project->releases->find({name => $releaseName});
error($c, "This project has no release named `$releaseName'.") unless $release;
error($c, "This build is already a part of release `$releaseName'.")
if $release->releasemembers->find({build => $build->id});
foreach my $output ($build->buildoutputs) {
error($c, "This build is no longer available.") unless isValidPath $output->path;
registerRoot $output->path;
}
$release->releasemembers->create({build => $build->id, description => $build->description});
$c->flash->{successMsg} = "Build added to project <tt>$releaseName</tt>.";
$c->res->redirect($c->uri_for($self->action_for("build"), $c->req->captures));
}
}
sub release : Chained('evalChain') PathPart('release') Args(0) {
my ($self, $c) = @_;
my $eval = $c->stash->{eval};
requireProjectOwner($c, $c->stash->{project});
my @builds = $eval->builds;
my $releaseName;
$releaseName ||= $_->releasename foreach @builds;
# If no release name has been defined by any of the builds, compose one of the project name and evaluation id
$releaseName = $eval->get_column('project') . "-" . $eval->id unless defined $releaseName;
my $release;
$c->model('DB')->schema->txn_do(sub {
$release = $c->stash->{project}->releases->create(
{ name => $releaseName
, timestamp => time
});
foreach my $build (@builds) {
$release->releasemembers->create(
{ build => $build->id
, description => $build->description
}) if $build->buildstatus == 0;
}
});
$c->res->redirect($c->uri_for($c->controller('Release')->action_for('view'),
[$c->stash->{project}->name, $release->name]));
sub create_release : Chained('projectChain') PathPart('create-release') Args(0) {
my ($self, $c) = @_;
requireProjectOwner($c, $c->stash->{project});
$c->stash->{template} = 'edit-release.tt';
$c->stash->{create} = 1;
}
sub create_release_submit : Chained('projectChain') PathPart('create-release/submit') Args(0) {
my ($self, $c) = @_;
requireProjectOwner($c, $c->stash->{project});
my $releaseName = $c->request->params->{name};
my $release;
$c->model('DB')->schema->txn_do(sub {
# Note: $releaseName is validated in updateRelease, which will
# abort the transaction if the name isn't valid.
$release = $c->stash->{project}->releases->create(
{ name => $releaseName
, timestamp => time
});
Hydra::Controller::Release::updateRelease($c, $release);
});
$c->res->redirect($c->uri_for($c->controller('Release')->action_for('view'),
[$c->stash->{project}->name, $release->name]));
}
use utf8;
package Hydra::Schema::ReleaseMembers;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
=head1 NAME
Hydra::Schema::ReleaseMembers
=cut
use strict;
use warnings;
use base 'DBIx::Class::Core';
=head1 COMPONENTS LOADED
=over 4
=item * L<Hydra::Component::ToJSON>
=back
=cut
__PACKAGE__->load_components("+Hydra::Component::ToJSON");
=head1 TABLE: C<releasemembers>
=cut
__PACKAGE__->table("releasemembers");
=head1 ACCESSORS
=head2 project
data_type: 'text'
is_foreign_key: 1
is_nullable: 0
=head2 release_
data_type: 'text'
is_foreign_key: 1
is_nullable: 0
=head2 build
data_type: 'integer'
is_foreign_key: 1
is_nullable: 0
=head2 description
data_type: 'text'
is_nullable: 1
=cut
__PACKAGE__->add_columns(
"project",
{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },
"release_",
{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },
"build",
{ data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
"description",
{ data_type => "text", is_nullable => 1 },
);
=head1 PRIMARY KEY
=over 4
=item * L</project>
=item * L</release_>
=item * L</build>
=back
=cut
__PACKAGE__->set_primary_key("project", "release_", "build");
=head1 RELATIONS
=head2 build
Type: belongs_to
Related object: L<Hydra::Schema::Builds>
=cut
__PACKAGE__->belongs_to(
"build",
"Hydra::Schema::Builds",
{ id => "build" },
{ is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" },
);
=head2 project
Type: belongs_to
Related object: L<Hydra::Schema::Projects>
=cut
__PACKAGE__->belongs_to(
"project",
"Hydra::Schema::Projects",
{ name => "project" },
{ is_deferrable => 0, on_delete => "CASCADE", on_update => "CASCADE" },
);
=head2 release
Type: belongs_to
Related object: L<Hydra::Schema::Releases>
=cut
__PACKAGE__->belongs_to(
"release",
"Hydra::Schema::Releases",
{ name => "release_", project => "project" },
{ is_deferrable => 0, on_delete => "CASCADE", on_update => "CASCADE" },
);
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2020-02-06 12:22:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:k4z2YeB4gRAeAP6hmR93sQ
1;
use utf8;
package Hydra::Schema::Releases;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
=head1 NAME
Hydra::Schema::Releases
=cut
use strict;
use warnings;
use base 'DBIx::Class::Core';
=head1 COMPONENTS LOADED
=over 4
=item * L<Hydra::Component::ToJSON>
=back
=cut
__PACKAGE__->load_components("+Hydra::Component::ToJSON");
=cut
__PACKAGE__->table("releases");
=head1 ACCESSORS
=head2 project
data_type: 'text'
is_foreign_key: 1
is_nullable: 0
=head2 name
data_type: 'text'
is_nullable: 0
=head2 timestamp
data_type: 'integer'
is_nullable: 0
=head2 description
data_type: 'text'
is_nullable: 1
=cut
__PACKAGE__->add_columns(
"project",
{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },
"name",
{ data_type => "text", is_nullable => 0 },
"timestamp",
{ data_type => "integer", is_nullable => 0 },
"description",
{ data_type => "text", is_nullable => 1 },
);
=head1 PRIMARY KEY
=over 4
=item * L</project>
=item * L</name>
=back
=cut
__PACKAGE__->set_primary_key("project", "name");
=head1 RELATIONS
=head2 project
Type: belongs_to
Related object: L<Hydra::Schema::Projects>
=cut
__PACKAGE__->belongs_to(
"project",
"Hydra::Schema::Projects",
{ name => "project" },
{ is_deferrable => 0, on_delete => "CASCADE", on_update => "NO ACTION" },
);
=head2 releasemembers
Type: has_many
Related object: L<Hydra::Schema::ReleaseMembers>
=cut
__PACKAGE__->has_many(
"releasemembers",
"Hydra::Schema::ReleaseMembers",
{
"foreign.project" => "self.project",
"foreign.release_" => "self.name",
},
undef,
);
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2020-02-06 12:22:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:b4M/tHOhsy234tgTf+wqjQ
1;
=head1 TABLE: C<releases>
=head2 releasemembers
Type: has_many
Related object: L<Hydra::Schema::ReleaseMembers>
=cut
__PACKAGE__->has_many(
"releasemembers",
"Hydra::Schema::ReleaseMembers",
{ "foreign.build" => "self.id" },
undef,
);
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2020-02-06 12:34:25
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:EEXlcKN/ydXJ129vT0jTUw
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2020-05-06 12:32:57
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:3IyFj/9Zf/hvmhBY4U/IBQ
{ "foreign.project" => "self.name" },
undef,
);
=head2 releasemembers
Type: has_many
Related object: L<Hydra::Schema::ReleaseMembers>
=cut
__PACKAGE__->has_many(
"releasemembers",
"Hydra::Schema::ReleaseMembers",
Type: has_many
Related object: L<Hydra::Schema::Releases>
=cut
__PACKAGE__->has_many(
"releases",
"Hydra::Schema::Releases",
{ "foreign.project" => "self.name" },
undef,
);
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2020-02-06 12:22:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:pcF/8351zyo9VL6N5eimdQ
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2020-05-06 12:32:57
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dEIVgrFGilPfITprs6nYuA
[% WRAPPER layout.tt title="Release $release.name" %]
[% PROCESS common.tt %]
[% USE HTML %]
<p><em>Released on [% INCLUDE renderDateTime timestamp =
release.timestamp %].</em> <a class="btn" href="[% c.uri_for('/release' project.name release.name "edit") %]"><i class="icon-edit"></i></a></p>
[% IF !members %]
<p><em>No builds have been added to this release yet.</em></p>
[% ELSE %]
[% FOREACH m IN members %]
<h3>
<a href="[% c.uri_for('/build' m.build.id) %]">
[% HTML.escape(m.description) %]
</a>
</h3>
[% INCLUDE renderProductList build=m.build %]
[% END %]
[% END %]
[% END %]
[% PROCESS "product-list.tt" %]
[% WRAPPER layout.tt title=(create ? "New release" : "Edit release ${release.name}") %]
[% PROCESS common.tt %]
[% USE HTML %]
<form class="form-horizontal" action="[% IF create %][% c.uri_for('/project' project.name 'create-release/submit') %][% ELSE %][% c.uri_for('/release' project.name release.name 'submit') %][% END %]" method="post">
<fieldset>
<div class="control-group">
<label class="control-label">Identifier</label>
<div class="controls">
<input type="text" class="span3" name="name" [% HTML.attributes(value => release.name) %]/>
</div>
</div>
<div class="control-group">
<label class="control-label">Description</label>
<div class="controls">
<input type="text" class="span3" name="description" [% HTML.attributes(value => release.description) %]/>
</div>
</div>
<h3>Release members</h3>
<p><em>Note:</em> to add a build to this release, go to the build’s
information page and click on “Add to release”.</p>
[% FOREACH m IN members %]
<div class="releaseMember control-group">
<label class="control-label">Build [% m.build.id %] Label</label>
<div class="controls">
<input type="text" class="span3" name="member-[% m.build.id %]-description" [% HTML.attributes(value => m.description) %]/>
<button class="btn btn-warning" type="button" onclick='$(this).parents(".releaseMember").remove()'><i class="icon-trash icon-white"></i></button>
</div>
</div>
[% END %]
<div class="form-actions">
<button type="submit" class="btn btn-primary">
<i class="icon-ok icon-white"></i>
[%IF create %]Create[% ELSE %]Apply changes[% END %]
</button>
[% IF !create %]
<button id="delete-release" type="submit" class="btn btn-danger" name="action" value="delete">
<i class="icon-trash icon-white"></i>
Delete this release
</button>
<script type="text/javascript">
$("#delete-release").click(function() {
return confirm("Are you sure you want to delete this release?");
});
</script>
[% END %]
</div>
</fieldset>
</form>
[% END %]
[% IF c.user_exists && available && project.releases %]
<div id="add-to-release" class="modal hide fade" tabindex="-1" role="dialog" aria-hidden="true">
<form class="form-horizontal" action="[% c.uri_for('/build' build.id 'add-to-release') %]" method="post">
<div class="modal-body">
<div class="control-group">
<label class="control-label">Add to release</label>
<div class="controls">
<select class="span2" name="name">
[% FOREACH r IN project.releases %]
<option>[% HTML.escape(r.name) %]</option>
[% END %]
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Add</button>
<button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
</div>
</form>
<div id="tabs-releases" class="tab-pane">
[% IF releases.size == 0 %]
<p><em>This project has no releases yet.</em></p>
[% ELSE %]
<p>This project has made the following releases:</p>
<table class="table table-condensed table-striped clickable-rows">
<thead>
<tr>
<th>Name</th>
<th>Date</th>
</tr>
</thead>
<tbody>
[% FOREACH release IN releases %]
<tr>
<td><a class="row-link" href="[% c.uri_for('/release' project.name release.name) %]"><tt>[% release.name %]</tt></a></td>
<td>[% INCLUDE renderDateTime timestamp = release.timestamp %]</td>
</tr>
[% END %]
</tbody>
</table>
[% END %]
</div>
# Keep every build in every release of every project.
print STDERR "*** looking for release members\n";
keepBuild($_, 0) foreach $db->resultset('Builds')->search_literal(
"exists (select 1 from releasemembers where build = me.id)",
{ order_by => ["project", "jobset", "job", "id"], columns => [ @columns ] });
-- A release is a named set of builds. The ReleaseMembers table lists
-- the builds that constitute each release.
create table Releases (
project text not null,
name text not null,
timestamp integer not null,
description text,
primary key (project, name),
foreign key (project) references Projects(name) on delete cascade
);
create table ReleaseMembers (
project text not null,
release_ text not null,
build integer not null,
description text,
primary key (project, release_, build),
foreign key (project) references Projects(name) on delete cascade on update cascade,
foreign key (project, release_) references Releases(project, name) on delete cascade on update cascade,
foreign key (build) references Builds(id)
);