forked from lix-project/hydra
Compare commits
7 commits
de44407300
...
b1db31d026
Author | SHA1 | Date | |
---|---|---|---|
Maximilian Bosch | b1db31d026 | ||
Maximilian Bosch | eb32395fea | ||
Maximilian Bosch | 3ee51dbe58 | ||
Maximilian Bosch | e987f74954 | ||
Maximilian Bosch | 1f802c008c | ||
Maximilian Bosch | 3a4e0d4917 | ||
Maximilian Bosch | 3517acc5ba |
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -5,3 +5,5 @@
|
||||||
/src/sql/tmp.sqlite
|
/src/sql/tmp.sqlite
|
||||||
result
|
result
|
||||||
result-*
|
result-*
|
||||||
|
.hydra-data
|
||||||
|
outputs
|
||||||
|
|
27
README.md
27
README.md
|
@ -78,11 +78,11 @@ $ nix-build
|
||||||
### Development Environment
|
### Development Environment
|
||||||
|
|
||||||
You can use the provided shell.nix to get a working development environment:
|
You can use the provided shell.nix to get a working development environment:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ nix-shell
|
$ nix develop
|
||||||
$ autoreconfPhase
|
[nix-shell]$ just setup
|
||||||
$ configurePhase # NOTE: not ./configure
|
[nix-shell]$ just install
|
||||||
$ make
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Executing Hydra During Development
|
### Executing Hydra During Development
|
||||||
|
@ -91,10 +91,9 @@ When working on new features or bug fixes you need to be able to run Hydra from
|
||||||
can be done using [foreman](https://github.com/ddollar/foreman):
|
can be done using [foreman](https://github.com/ddollar/foreman):
|
||||||
|
|
||||||
```
|
```
|
||||||
$ nix-shell
|
$ nix develop
|
||||||
$ # hack hack
|
[nix-shell]$ just install
|
||||||
$ make
|
[nix-shell]$ foreman start
|
||||||
$ foreman start
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Have a look at the [Procfile](./Procfile) if you want to see how the processes are being started. In order to avoid
|
Have a look at the [Procfile](./Procfile) if you want to see how the processes are being started. In order to avoid
|
||||||
|
@ -115,22 +114,22 @@ Start by following the steps in [Development Environment](#development-environme
|
||||||
Then, you can run the tests and the perlcritic linter together with:
|
Then, you can run the tests and the perlcritic linter together with:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix-shell
|
$ nix develop
|
||||||
$ make check
|
[nix-shell]$ just test
|
||||||
```
|
```
|
||||||
|
|
||||||
You can run a single test with:
|
You can run a single test with:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ nix-shell
|
$ nix develop
|
||||||
$ yath test ./t/foo/bar.t
|
[nix-shell]$ yath test ./t/foo/bar.t
|
||||||
```
|
```
|
||||||
|
|
||||||
And you can run just perlcritic with:
|
And you can run just perlcritic with:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ nix-shell
|
$ nix develop
|
||||||
$ make perlcritic
|
[nix-shell]$ just perlcritic
|
||||||
```
|
```
|
||||||
|
|
||||||
### JSON API
|
### JSON API
|
||||||
|
|
|
@ -1,122 +0,0 @@
|
||||||
* Recreating the schema bindings:
|
|
||||||
|
|
||||||
$ make -C src/sql update-dbix
|
|
||||||
|
|
||||||
* Running the test server:
|
|
||||||
|
|
||||||
$ DBIC_TRACE=1 ./script/hydra_server.pl
|
|
||||||
|
|
||||||
* Setting the maximum number of concurrent builds per system type:
|
|
||||||
|
|
||||||
$ psql -d hydra <<< "insert into SystemTypes(system, maxConcurrent) values('i686-linux', 3);"
|
|
||||||
|
|
||||||
* Creating a user:
|
|
||||||
|
|
||||||
$ hydra-create-user root --email-address 'e.dolstra@tudelft.nl' \
|
|
||||||
--password-prompt
|
|
||||||
|
|
||||||
(Replace "foobar" with the desired password.)
|
|
||||||
|
|
||||||
To make the user an admin:
|
|
||||||
|
|
||||||
$ hydra-create-user root --role admin
|
|
||||||
|
|
||||||
To enable a non-admin user to create projects:
|
|
||||||
|
|
||||||
$ hydra-create-user root --role create-projects
|
|
||||||
|
|
||||||
* Changing the priority of a scheduled build:
|
|
||||||
|
|
||||||
update buildschedulinginfo set priority = 200 where id = <ID>;
|
|
||||||
|
|
||||||
* Changing the priority of all builds for a jobset:
|
|
||||||
|
|
||||||
update buildschedulinginfo set priority = 20 where id in (select id from builds where finished = 0 and project = 'nixpkgs' and jobset = 'trunk');
|
|
||||||
|
|
||||||
|
|
||||||
* Steps to install:
|
|
||||||
|
|
||||||
- Install the Hydra closure.
|
|
||||||
|
|
||||||
- Set HYDRA_DATA to /somewhere.
|
|
||||||
|
|
||||||
- Run hydra_init.pl
|
|
||||||
|
|
||||||
- Start hydra_server
|
|
||||||
|
|
||||||
- Visit http://localhost:3000/
|
|
||||||
|
|
||||||
- Create a user (see above)
|
|
||||||
|
|
||||||
- Create a project, jobset etc.
|
|
||||||
|
|
||||||
- Start hydra_evaluator and hydra_queue_runner
|
|
||||||
|
|
||||||
|
|
||||||
* Job selection:
|
|
||||||
|
|
||||||
php-sat:build [system = "i686-linux"]
|
|
||||||
php-sat:build [same system]
|
|
||||||
tarball [same patchelfSrc]
|
|
||||||
--if system i686-linux --arg build {...}
|
|
||||||
|
|
||||||
|
|
||||||
* Restart all aborted builds in a given evaluation (e.g. 820909):
|
|
||||||
|
|
||||||
> update builds set finished = 0 where id in (select id from builds where finished = 1 and buildstatus = 3 and exists (select 1 from jobsetevalmembers where eval = 820909 and build = id));
|
|
||||||
|
|
||||||
|
|
||||||
* Restart all builds in a given evaluation that had a build step time out:
|
|
||||||
|
|
||||||
> update builds set finished = 0 where id in (select id from builds where finished = 1 and buildstatus != 0 and exists (select 1 from jobsetevalmembers where eval = 926992 and build = id) and exists (select 1 from buildsteps where build = id and status = 7));
|
|
||||||
|
|
||||||
|
|
||||||
* select * from (select project, jobset, job, system, max(timestamp) timestamp from builds where finished = 1 group by project, jobset, job, system) x join builds y on x.timestamp = y.timestamp and x.project = y.project and x.jobset = y.jobset and x.job = y.job and x.system = y.system;
|
|
||||||
|
|
||||||
select * from (select project, jobset, job, system, max(timestamp) timestamp from builds where finished = 1 group by project, jobset, job, system) natural join builds;
|
|
||||||
|
|
||||||
|
|
||||||
* Delete all scheduled builds that are not already building:
|
|
||||||
|
|
||||||
delete from builds where finished = 0 and not exists (select 1 from buildschedulinginfo s where s.id = builds.id and busy != 0);
|
|
||||||
|
|
||||||
|
|
||||||
* select x.project, x.jobset, x.job, x.system, x.id, x.timestamp, r.buildstatus, b.id, b.timestamp
|
|
||||||
from (select project, jobset, job, system, max(id) as id from Builds where finished = 1 group by project, jobset, job, system) as a_
|
|
||||||
natural join Builds x
|
|
||||||
natural join BuildResultInfo r
|
|
||||||
left join Builds b on b.id =
|
|
||||||
(select max(id) from builds c
|
|
||||||
natural join buildresultinfo r2
|
|
||||||
where x.project = c.project and x.jobset = c.jobset and x.job = c.job and x.system = c.system
|
|
||||||
and x.id > c.id and r.buildstatus != r2.buildstatus);
|
|
||||||
|
|
||||||
* Using PostgreSQL (version 9.2 or newer is required):
|
|
||||||
|
|
||||||
$ HYDRA_DBI="dbi:Pg:dbname=hydra;" hydra-server
|
|
||||||
|
|
||||||
|
|
||||||
* Find the builds with the highest number of build steps:
|
|
||||||
|
|
||||||
select id, (select count(*) from buildsteps where build = x.id) as n from builds x order by n desc;
|
|
||||||
|
|
||||||
|
|
||||||
* Evaluating the NixOS Hydra jobs:
|
|
||||||
|
|
||||||
$ ./hydra_eval_jobs ~/Dev/nixos-wc/release.nix --arg nixpkgs '{outPath = /home/eelco/Dev/nixpkgs-wc;}' --arg nixosSrc '{outPath = /home/eelco/Dev/nixos-wc; rev = 1234;}' --arg services '{outhPath = /home/eelco/services-wc;}' --argstr system i686-linux --argstr system x86_64-linux --arg officialRelease false
|
|
||||||
|
|
||||||
|
|
||||||
* Show all the failing jobs/systems in the nixpkgs:stdenv jobset that
|
|
||||||
succeed in the nixpkgs:trunk jobset:
|
|
||||||
|
|
||||||
select job, system from builds b natural join buildresultinfo where project = 'nixpkgs' and jobset = 'stdenv' and iscurrent = 1 and finished = 1 and buildstatus != 0 and exists (select 1 from builds natural join buildresultinfo where project = 'nixpkgs' and jobset = 'trunk' and job = b.job and system = b.system and iscurrent = 1 and finished = 1 and buildstatus = 0) order by job, system;
|
|
||||||
|
|
||||||
|
|
||||||
* Get all Nixpkgs jobs that have never built succesfully:
|
|
||||||
|
|
||||||
select project, jobset, job from builds b1
|
|
||||||
where project = 'nixpkgs' and jobset = 'trunk' and iscurrent = 1
|
|
||||||
group by project, jobset, job
|
|
||||||
having not exists
|
|
||||||
(select 1 from builds b2 where b1.project = b2.project and b1.jobset = b2.jobset and b1.job = b2.job and finished = 1 and buildstatus = 0)
|
|
||||||
order by project, jobset, job;
|
|
|
@ -12,15 +12,14 @@ To enter a shell in which all environment variables (such as `PERL5LIB`)
|
||||||
and dependencies can be found:
|
and dependencies can be found:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix-shell
|
$ nix develop
|
||||||
```
|
```
|
||||||
|
|
||||||
To build Hydra, you should then do:
|
To build Hydra, you should then do:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
[nix-shell]$ autoreconfPhase
|
[nix-shell]$ just setup
|
||||||
[nix-shell]$ configurePhase
|
[nix-shell]$ just install
|
||||||
[nix-shell]$ make
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You start a local database, the webserver, and other components with
|
You start a local database, the webserver, and other components with
|
||||||
|
@ -41,18 +40,13 @@ $ ./src/script/hydra-server
|
||||||
You can run Hydra's test suite with the following:
|
You can run Hydra's test suite with the following:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
[nix-shell]$ make check
|
[nix-shell]$ just test
|
||||||
[nix-shell]$ # to run as many tests as you have cores:
|
|
||||||
[nix-shell]$ make check YATH_JOB_COUNT=$NIX_BUILD_CORES
|
|
||||||
[nix-shell]$ # or run yath directly:
|
[nix-shell]$ # or run yath directly:
|
||||||
[nix-shell]$ yath test
|
[nix-shell]$ yath test
|
||||||
[nix-shell]$ # to run as many tests as you have cores:
|
[nix-shell]$ # to run as many tests as you have cores:
|
||||||
[nix-shell]$ yath test -j $NIX_BUILD_CORES
|
[nix-shell]$ yath test -j $NIX_BUILD_CORES
|
||||||
```
|
```
|
||||||
|
|
||||||
When using `yath` instead of `make check`, ensure you have run `make`
|
|
||||||
in the root of the repository at least once.
|
|
||||||
|
|
||||||
**Warning**: Currently, the tests can fail
|
**Warning**: Currently, the tests can fail
|
||||||
if run with high parallelism [due to an issue in
|
if run with high parallelism [due to an issue in
|
||||||
`Test::PostgreSQL`](https://github.com/TJC/Test-postgresql/issues/40)
|
`Test::PostgreSQL`](https://github.com/TJC/Test-postgresql/issues/40)
|
||||||
|
@ -103,3 +97,14 @@ Off NixOS, change `/etc/nix/nix.conf`:
|
||||||
```conf
|
```conf
|
||||||
trusted-users = root YOURUSERNAME
|
trusted-users = root YOURUSERNAME
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Updating schema bindings
|
||||||
|
|
||||||
|
```
|
||||||
|
just update-dbix
|
||||||
|
```
|
||||||
|
|
||||||
|
### Find the builds with the highest number of build steps:
|
||||||
|
|
||||||
|
select id, (select count(*) from buildsteps where build = x.id) as n from builds x order by n desc;
|
||||||
|
|
||||||
|
|
|
@ -312,6 +312,21 @@ Declarative Projects
|
||||||
|
|
||||||
see this [chapter](./plugins/declarative-projects.md)
|
see this [chapter](./plugins/declarative-projects.md)
|
||||||
|
|
||||||
|
Private Projects
|
||||||
|
----------------
|
||||||
|
|
||||||
|
By checking the `Private` checkbox in the project creation form, a project
|
||||||
|
and everything related to it (jobsets, evals, builds, etc.) can only be accessed
|
||||||
|
if a user is authenticated. Otherwise, a 404 will be returned by the API and Web
|
||||||
|
UI. This is the main difference to "hidden" projects where everything can
|
||||||
|
be obtained if the URLs are known.
|
||||||
|
|
||||||
|
Please note that the store paths that are realized in evaluations that belong to
|
||||||
|
private projects aren't protected! It is assumed that the hashes are unknown
|
||||||
|
and thus inaccessible. For a real protection of the binary cache it's recommended
|
||||||
|
to either use `nix.sshServe` instead or to protect the routes `/nar/*` and `*.narinfo`
|
||||||
|
with a reverse proxy.
|
||||||
|
|
||||||
Email Notifications
|
Email Notifications
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
|
34
flake.lock
34
flake.lock
|
@ -24,11 +24,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1719994518,
|
"lastModified": 1722555600,
|
||||||
"narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=",
|
"narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7",
|
"rev": "8471fe90ad337a8074e957b69ca4d0089218391d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -48,11 +48,11 @@
|
||||||
"pre-commit-hooks": "pre-commit-hooks"
|
"pre-commit-hooks": "pre-commit-hooks"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1723331518,
|
"lastModified": 1723919517,
|
||||||
"narHash": "sha256-JVnQ3OLbXQAlkOluFc3gWhZMbhared1Rg5YvNEc92m0=",
|
"narHash": "sha256-D6+zmRXzr85p7riphuIrJQqangoJe70XM5jHhMWwXws=",
|
||||||
"ref": "refs/heads/main",
|
"ref": "refs/heads/main",
|
||||||
"rev": "5137cea99044d54337e439510a647743110b2d7d",
|
"rev": "278fddc317cf0cf4d3602d0ec0f24d1dd281fadb",
|
||||||
"revCount": 16128,
|
"revCount": 16138,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.lix.systems/lix-project/lix"
|
"url": "https://git.lix.systems/lix-project/lix"
|
||||||
},
|
},
|
||||||
|
@ -74,11 +74,11 @@
|
||||||
"treefmt-nix": "treefmt-nix"
|
"treefmt-nix": "treefmt-nix"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1721195872,
|
"lastModified": 1723579251,
|
||||||
"narHash": "sha256-TlvRq634MSl22BWLmpTy2vdtKntbZlsUwdMq8Mp9AWs=",
|
"narHash": "sha256-xnHtfw0gRhV+2S9U7hQwvp2klTy1Iv7FlMMO0/WiMVc=",
|
||||||
"ref": "refs/heads/main",
|
"ref": "refs/heads/main",
|
||||||
"rev": "c057494450f2d1420726ddb0bab145a5ff4ddfdd",
|
"rev": "42a160bce2fd9ffebc3809746bc80cc7208f9b08",
|
||||||
"revCount": 608,
|
"revCount": 609,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.lix.systems/lix-project/nix-eval-jobs"
|
"url": "https://git.lix.systems/lix-project/nix-eval-jobs"
|
||||||
},
|
},
|
||||||
|
@ -126,11 +126,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1723282977,
|
"lastModified": 1723688146,
|
||||||
"narHash": "sha256-oTK91aOlA/4IsjNAZGMEBz7Sq1zBS0Ltu4/nIQdYDOg=",
|
"narHash": "sha256-sqLwJcHYeWLOeP/XoLwAtYjr01TISlkOfz+NG82pbdg=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "a781ff33ae258bbcfd4ed6e673860c3e923bf2cc",
|
"rev": "c3d4ac725177c030b1e289015989da2ad9d56af0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -187,11 +187,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1721059077,
|
"lastModified": 1723454642,
|
||||||
"narHash": "sha256-gCICMMX7VMSKKt99giDDtRLkHJ0cwSgBtDijJAqTlto=",
|
"narHash": "sha256-S0Gvsenh0II7EAaoc9158ZB4vYyuycvMGKGxIbERNAM=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "treefmt-nix",
|
"repo": "treefmt-nix",
|
||||||
"rev": "0fb28f237f83295b4dd05e342f333b447c097398",
|
"rev": "349de7bc435bdff37785c2466f054ed1766173be",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
# wait for hydra-server to listen
|
# wait for hydra-server to listen
|
||||||
while ! nc -z localhost 63333; do sleep 1; done
|
while ! nc -z localhost 63333; do sleep 1; done
|
||||||
|
|
||||||
HYDRA_CONFIG=$(pwd)/.hydra-data/hydra.conf exec hydra-evaluator
|
HYDRA_CONFIG=$(pwd)/.hydra-data/hydra.conf exec $(pwd)/outputs/out/bin/hydra-evaluator
|
||||||
|
|
|
@ -28,4 +28,4 @@ use-substitutes = true
|
||||||
</hydra_notify>
|
</hydra_notify>
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
HYDRA_CONFIG=$(pwd)/.hydra-data/hydra.conf exec hydra-dev-server --port 63333 --restart --debug
|
HYDRA_CONFIG=$(pwd)/.hydra-data/hydra.conf exec $(pwd)/outputs/out/bin/hydra-dev-server --port 63333 --restart --debug
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
# wait for hydra-server to listen
|
# wait for hydra-server to listen
|
||||||
while ! nc -z localhost 63333; do sleep 1; done
|
while ! nc -z localhost 63333; do sleep 1; done
|
||||||
|
|
||||||
HYDRA_CONFIG=$(pwd)/.hydra-data/hydra.conf exec hydra-notify
|
HYDRA_CONFIG=$(pwd)/.hydra-data/hydra.conf exec $(pwd)/outputs/out/bin/hydra-notify
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
# wait until hydra is listening on port 63333
|
# wait until hydra is listening on port 63333
|
||||||
while ! nc -z localhost 63333; do sleep 1; done
|
while ! nc -z localhost 63333; do sleep 1; done
|
||||||
|
|
||||||
NIX_REMOTE_SYSTEMS="" HYDRA_CONFIG=$(pwd)/.hydra-data/hydra.conf exec hydra-queue-runner
|
NIX_REMOTE_SYSTEMS="" HYDRA_CONFIG=$(pwd)/.hydra-data/hydra.conf exec $(pwd)/outputs/out/bin/hydra-queue-runner
|
||||||
|
|
|
@ -184,6 +184,9 @@ paths:
|
||||||
visible:
|
visible:
|
||||||
description: when set to true the project is displayed in the web interface
|
description: when set to true the project is displayed in the web interface
|
||||||
type: boolean
|
type: boolean
|
||||||
|
private:
|
||||||
|
description: when set to true the project and all related objects are only accessible to authenticated users
|
||||||
|
type: boolean
|
||||||
declarative:
|
declarative:
|
||||||
description: declarative input configured for this project
|
description: declarative input configured for this project
|
||||||
type: object
|
type: object
|
||||||
|
@ -625,6 +628,9 @@ components:
|
||||||
hidden:
|
hidden:
|
||||||
description: when set to true the project is not displayed in the web interface
|
description: when set to true the project is not displayed in the web interface
|
||||||
type: boolean
|
type: boolean
|
||||||
|
private:
|
||||||
|
description: when set to true the project and all related objects are only accessible to authenticated users
|
||||||
|
type: boolean
|
||||||
enabled:
|
enabled:
|
||||||
description: when set to true the project gets scheduled for evaluation
|
description: when set to true the project gets scheduled for evaluation
|
||||||
type: boolean
|
type: boolean
|
||||||
|
|
17
justfile
Normal file
17
justfile
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
setup *OPTIONS:
|
||||||
|
meson setup build --prefix="$PWD/outputs/out" $mesonFlags {{ OPTIONS }}
|
||||||
|
|
||||||
|
build *OPTIONS:
|
||||||
|
meson compile -C build {{ OPTIONS }}
|
||||||
|
|
||||||
|
install *OPTIONS: (build OPTIONS)
|
||||||
|
meson install -C build
|
||||||
|
|
||||||
|
test *OPTIONS:
|
||||||
|
meson test -C build --print-errorlogs {{ OPTIONS }}
|
||||||
|
|
||||||
|
update-dbix:
|
||||||
|
cd src/sql && ./update-dbix-harness.sh
|
||||||
|
|
||||||
|
perlcritic:
|
||||||
|
perlcritic .
|
|
@ -37,6 +37,7 @@
|
||||||
|
|
||||||
, cacert
|
, cacert
|
||||||
, foreman
|
, foreman
|
||||||
|
, just
|
||||||
, glibcLocales
|
, glibcLocales
|
||||||
, libressl
|
, libressl
|
||||||
, openldap
|
, openldap
|
||||||
|
@ -97,6 +98,7 @@ let
|
||||||
FileLibMagic
|
FileLibMagic
|
||||||
FileSlurper
|
FileSlurper
|
||||||
FileWhich
|
FileWhich
|
||||||
|
HTMLTreeBuilderXPath
|
||||||
IOCompress
|
IOCompress
|
||||||
IPCRun
|
IPCRun
|
||||||
IPCRun3
|
IPCRun3
|
||||||
|
@ -190,6 +192,8 @@ stdenv.mkDerivation (finalAttrs: {
|
||||||
postgresql_13
|
postgresql_13
|
||||||
pixz
|
pixz
|
||||||
nix-eval-jobs
|
nix-eval-jobs
|
||||||
|
perlPackages.PLS
|
||||||
|
just
|
||||||
];
|
];
|
||||||
|
|
||||||
checkInputs = [
|
checkInputs = [
|
||||||
|
@ -233,8 +237,8 @@ stdenv.mkDerivation (finalAttrs: {
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
pushd $(git rev-parse --show-toplevel) >/dev/null
|
pushd $(git rev-parse --show-toplevel) >/dev/null
|
||||||
|
|
||||||
PATH=$(pwd)/src/hydra-evaluator:$(pwd)/src/script:$(pwd)/src/hydra-queue-runner:$PATH
|
PATH=$(pwd)/outputs/out/bin:$PATH
|
||||||
PERL5LIB=$(pwd)/src/lib:$PERL5LIB
|
PERL5LIB=$(pwd)/src/lib:$(pwd)/t/lib:$PERL5LIB
|
||||||
export HYDRA_HOME="$(pwd)/src/"
|
export HYDRA_HOME="$(pwd)/src/"
|
||||||
mkdir -p .hydra-data
|
mkdir -p .hydra-data
|
||||||
export HYDRA_DATA="$(pwd)/.hydra-data"
|
export HYDRA_DATA="$(pwd)/.hydra-data"
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
# IMPORTANT: if you delete this file your app will not work as
|
|
||||||
# expected. you have been warned
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
use inc::Module::Install;
|
|
||||||
|
|
||||||
name 'Hydra';
|
|
||||||
all_from 'lib/Hydra.pm';
|
|
||||||
|
|
||||||
requires 'Catalyst::Runtime' => '5.7015';
|
|
||||||
requires 'Catalyst::Plugin::ConfigLoader';
|
|
||||||
requires 'Catalyst::Plugin::Static::Simple';
|
|
||||||
requires 'Catalyst::Action::RenderView';
|
|
||||||
requires 'parent';
|
|
||||||
requires 'Config::General'; # This should reflect the config file format you've chosen
|
|
||||||
# See Catalyst::Plugin::ConfigLoader for supported formats
|
|
||||||
catalyst;
|
|
||||||
|
|
||||||
install_script glob('script/*.pl');
|
|
||||||
auto_install;
|
|
||||||
WriteAll;
|
|
|
@ -23,23 +23,37 @@ sub all : Chained('get_builds') PathPart {
|
||||||
$c->stash->{total} = $c->stash->{allBuilds}->search({finished => 1})->count
|
$c->stash->{total} = $c->stash->{allBuilds}->search({finished => 1})->count
|
||||||
unless defined $c->stash->{total};
|
unless defined $c->stash->{total};
|
||||||
|
|
||||||
$c->stash->{builds} = [ $c->stash->{allBuilds}->search(
|
my $extra = {
|
||||||
{ finished => 1 },
|
order_by => "stoptime DESC"
|
||||||
{ order_by => "stoptime DESC"
|
|
||||||
, columns => [@buildListColumns]
|
, columns => [@buildListColumns]
|
||||||
, rows => $resultsPerPage
|
, rows => $resultsPerPage
|
||||||
, page => $page }) ];
|
, page => $page };
|
||||||
|
|
||||||
|
my $criteria = { finished => 1 };
|
||||||
|
|
||||||
|
unless ($c->user_exists) {
|
||||||
|
$extra->{join} = {"jobset" => "project"};
|
||||||
|
$criteria->{"project.private"} = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$c->stash->{builds} = [ $c->stash->{allBuilds}->search(
|
||||||
|
$criteria,
|
||||||
|
$extra
|
||||||
|
) ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub nix : Chained('get_builds') PathPart('channel/latest') CaptureArgs(0) {
|
sub nix : Chained('get_builds') PathPart('channel/latest') CaptureArgs(0) {
|
||||||
my ($self, $c) = @_;
|
my ($self, $c) = @_;
|
||||||
|
|
||||||
|
my $private = $c->user_exists ? [1,0] : [0];
|
||||||
|
|
||||||
$c->stash->{channelName} = $c->stash->{channelBaseName} . "-latest";
|
$c->stash->{channelName} = $c->stash->{channelBaseName} . "-latest";
|
||||||
$c->stash->{channelBuilds} = $c->stash->{latestSucceeded}
|
$c->stash->{channelBuilds} = $c->stash->{latestSucceeded}
|
||||||
->search_literal("exists (select 1 from buildproducts where build = me.id and type = 'nix-build')")
|
->search_literal("exists (select 1 from buildproducts where build = me.id and type = 'nix-build')")
|
||||||
->search({}, { columns => [@buildListColumns, 'drvpath', 'description', 'homepage']
|
->search({"project.private" => {-in => $private}},
|
||||||
, join => ["buildoutputs"]
|
{ columns => [@buildListColumns, 'drvpath', 'description', 'homepage']
|
||||||
|
, join => ["buildoutputs", {"jobset" => "project"}]
|
||||||
, order_by => ["me.id", "buildoutputs.name"]
|
, order_by => ["me.id", "buildoutputs.name"]
|
||||||
, '+select' => ['buildoutputs.path', 'buildoutputs.name'], '+as' => ['outpath', 'outname'] });
|
, '+select' => ['buildoutputs.path', 'buildoutputs.name'], '+as' => ['outpath', 'outname'] });
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,8 @@ sub latestbuilds : Chained('api') PathPart('latestbuilds') Args(0) {
|
||||||
error($c, "Parameter not defined!") if !defined $nr;
|
error($c, "Parameter not defined!") if !defined $nr;
|
||||||
|
|
||||||
my $project = $c->request->params->{project};
|
my $project = $c->request->params->{project};
|
||||||
|
checkProjectVisibleForGuest($c, $c->stash->{project});
|
||||||
|
|
||||||
my $jobset = $c->request->params->{jobset};
|
my $jobset = $c->request->params->{jobset};
|
||||||
my $job = $c->request->params->{job};
|
my $job = $c->request->params->{job};
|
||||||
my $system = $c->request->params->{system};
|
my $system = $c->request->params->{system};
|
||||||
|
@ -106,6 +108,8 @@ sub jobsets : Chained('api') PathPart('jobsets') Args(0) {
|
||||||
my $project = $c->model('DB::Projects')->find($projectName)
|
my $project = $c->model('DB::Projects')->find($projectName)
|
||||||
or notFound($c, "Project $projectName doesn't exist.");
|
or notFound($c, "Project $projectName doesn't exist.");
|
||||||
|
|
||||||
|
checkProjectVisibleForGuest($c, $project);
|
||||||
|
|
||||||
my @jobsets = jobsetOverview($c, $project);
|
my @jobsets = jobsetOverview($c, $project);
|
||||||
|
|
||||||
my @list;
|
my @list;
|
||||||
|
@ -124,7 +128,17 @@ sub queue : Chained('api') PathPart('queue') Args(0) {
|
||||||
my $nr = $c->request->params->{nr};
|
my $nr = $c->request->params->{nr};
|
||||||
error($c, "Parameter not defined!") if !defined $nr;
|
error($c, "Parameter not defined!") if !defined $nr;
|
||||||
|
|
||||||
my @builds = $c->model('DB::Builds')->search({finished => 0}, {rows => $nr, order_by => ["priority DESC", "id"]});
|
my $criteria = {finished => 0};
|
||||||
|
my $extra = {
|
||||||
|
rows => $nr,
|
||||||
|
order_by => ["priority DESC", "id"]
|
||||||
|
};
|
||||||
|
unless ($c->user_exists) {
|
||||||
|
$criteria->{"project.private"} = 0;
|
||||||
|
$extra->{join} = {"jobset" => "project"};
|
||||||
|
}
|
||||||
|
|
||||||
|
my @builds = $c->model('DB::Builds')->search($criteria, $extra);
|
||||||
|
|
||||||
my @list;
|
my @list;
|
||||||
push @list, buildToHash($_) foreach @builds;
|
push @list, buildToHash($_) foreach @builds;
|
||||||
|
@ -198,6 +212,16 @@ sub scmdiff : Path('/api/scmdiff') Args(0) {
|
||||||
my $rev1 = $c->request->params->{rev1};
|
my $rev1 = $c->request->params->{rev1};
|
||||||
my $rev2 = $c->request->params->{rev2};
|
my $rev2 = $c->request->params->{rev2};
|
||||||
|
|
||||||
|
unless ($c->user_exists) {
|
||||||
|
my $search = $c->model('DB::JobsetEvalInputs')->search(
|
||||||
|
{ "project.private" => 0, "me.uri" => $uri },
|
||||||
|
{ join => { "eval" => { jobset => "project" } } }
|
||||||
|
);
|
||||||
|
if ($search == 0) {
|
||||||
|
die("invalid revisions: [$rev1] [$rev2]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
die("invalid revisions: [$rev1] [$rev2]") if $rev1 !~ m/^[a-zA-Z0-9_.]+$/ || $rev2 !~ m/^[a-zA-Z0-9_.]+$/;
|
die("invalid revisions: [$rev1] [$rev2]") if $rev1 !~ m/^[a-zA-Z0-9_.]+$/ || $rev2 !~ m/^[a-zA-Z0-9_.]+$/;
|
||||||
|
|
||||||
# FIXME: injection danger.
|
# FIXME: injection danger.
|
||||||
|
|
|
@ -39,6 +39,9 @@ sub buildChain :Chained('/') :PathPart('build') :CaptureArgs(1) {
|
||||||
$c->stash->{project} = $c->stash->{build}->project;
|
$c->stash->{project} = $c->stash->{build}->project;
|
||||||
$c->stash->{jobset} = $c->stash->{build}->jobset;
|
$c->stash->{jobset} = $c->stash->{build}->jobset;
|
||||||
$c->stash->{job} = $c->stash->{build}->job;
|
$c->stash->{job} = $c->stash->{build}->job;
|
||||||
|
|
||||||
|
checkProjectVisibleForGuest($c, $c->stash->{project});
|
||||||
|
|
||||||
$c->stash->{runcommandlogs} = [$c->stash->{build}->runcommandlogs->search({}, {order_by => ["id DESC"]})];
|
$c->stash->{runcommandlogs} = [$c->stash->{build}->runcommandlogs->search({}, {order_by => ["id DESC"]})];
|
||||||
|
|
||||||
$c->stash->{runcommandlogProblem} = undef;
|
$c->stash->{runcommandlogProblem} = undef;
|
||||||
|
|
|
@ -3,6 +3,7 @@ package Hydra::Controller::Channel;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use base 'Hydra::Base::Controller::REST';
|
use base 'Hydra::Base::Controller::REST';
|
||||||
|
use Hydra::Helper::CatalystUtils;
|
||||||
|
|
||||||
|
|
||||||
sub channel : Chained('/') PathPart('channel/custom') CaptureArgs(3) {
|
sub channel : Chained('/') PathPart('channel/custom') CaptureArgs(3) {
|
||||||
|
@ -10,6 +11,8 @@ sub channel : Chained('/') PathPart('channel/custom') CaptureArgs(3) {
|
||||||
|
|
||||||
$c->stash->{project} = $c->model('DB::Projects')->find($projectName);
|
$c->stash->{project} = $c->model('DB::Projects')->find($projectName);
|
||||||
|
|
||||||
|
checkProjectVisibleForGuest($c, $c->stash->{project});
|
||||||
|
|
||||||
notFound($c, "Project $projectName doesn't exist.")
|
notFound($c, "Project $projectName doesn't exist.")
|
||||||
if !$c->stash->{project};
|
if !$c->stash->{project};
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,8 @@ sub job : Chained('/') PathPart('job') CaptureArgs(3) {
|
||||||
|
|
||||||
$c->stash->{job} = $jobName;
|
$c->stash->{job} = $jobName;
|
||||||
$c->stash->{project} = $c->stash->{jobset}->project;
|
$c->stash->{project} = $c->stash->{jobset}->project;
|
||||||
|
|
||||||
|
checkProjectVisibleForGuest($c, $c->stash->{project});
|
||||||
}
|
}
|
||||||
|
|
||||||
sub shield :Chained('job') PathPart('shield') Args(0) {
|
sub shield :Chained('job') PathPart('shield') Args(0) {
|
||||||
|
|
|
@ -17,6 +17,8 @@ sub jobsetChain :Chained('/') :PathPart('jobset') :CaptureArgs(2) {
|
||||||
|
|
||||||
$c->stash->{project} = $project;
|
$c->stash->{project} = $project;
|
||||||
|
|
||||||
|
checkProjectVisibleForGuest($c, $c->stash->{project});
|
||||||
|
|
||||||
$c->stash->{jobset} = $project->jobsets->find({ name => $jobsetName });
|
$c->stash->{jobset} = $project->jobsets->find({ name => $jobsetName });
|
||||||
|
|
||||||
if (!$c->stash->{jobset} && !($c->action->name eq "jobset" and $c->request->method eq "PUT")) {
|
if (!$c->stash->{jobset} && !($c->action->name eq "jobset" and $c->request->method eq "PUT")) {
|
||||||
|
@ -294,26 +296,24 @@ sub updateJobset {
|
||||||
# Set the inputs of this jobset.
|
# Set the inputs of this jobset.
|
||||||
$jobset->jobsetinputs->delete;
|
$jobset->jobsetinputs->delete;
|
||||||
|
|
||||||
if ($type == 0) {
|
foreach my $name (keys %{$c->stash->{params}->{inputs}}) {
|
||||||
foreach my $name (keys %{$c->stash->{params}->{inputs}}) {
|
my $inputData = $c->stash->{params}->{inputs}->{$name};
|
||||||
my $inputData = $c->stash->{params}->{inputs}->{$name};
|
my $type = $inputData->{type};
|
||||||
my $type = $inputData->{type};
|
my $value = $inputData->{value};
|
||||||
my $value = $inputData->{value};
|
my $emailresponsible = defined $inputData->{emailresponsible} ? 1 : 0;
|
||||||
my $emailresponsible = defined $inputData->{emailresponsible} ? 1 : 0;
|
my $types = knownInputTypes($c);
|
||||||
my $types = knownInputTypes($c);
|
|
||||||
|
|
||||||
badRequest($c, "Invalid input name ‘$name’.") unless $name =~ /^[[:alpha:]][\w-]*$/;
|
badRequest($c, "Invalid input name ‘$name’.") unless $name =~ /^[[:alpha:]][\w-]*$/;
|
||||||
badRequest($c, "Invalid input type ‘$type’; valid types: $types.") unless defined $c->stash->{inputTypes}->{$type};
|
badRequest($c, "Invalid input type ‘$type’; valid types: $types.") unless defined $c->stash->{inputTypes}->{$type};
|
||||||
|
|
||||||
my $input = $jobset->jobsetinputs->create(
|
my $input = $jobset->jobsetinputs->create(
|
||||||
{ name => $name,
|
{ name => $name,
|
||||||
type => $type,
|
type => $type,
|
||||||
emailresponsible => $emailresponsible
|
emailresponsible => $emailresponsible
|
||||||
});
|
});
|
||||||
|
|
||||||
$value = checkInputValue($c, $name, $type, $value);
|
$value = checkInputValue($c, $name, $type, $value);
|
||||||
$input->jobsetinputalts->create({altnr => 0, value => $value});
|
$input->jobsetinputalts->create({altnr => 0, value => $value});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@ sub evalChain : Chained('/') PathPart('eval') CaptureArgs(1) {
|
||||||
$c->stash->{eval} = $eval;
|
$c->stash->{eval} = $eval;
|
||||||
$c->stash->{jobset} = $eval->jobset;
|
$c->stash->{jobset} = $eval->jobset;
|
||||||
$c->stash->{project} = $eval->jobset->project;
|
$c->stash->{project} = $eval->jobset->project;
|
||||||
|
|
||||||
|
checkProjectVisibleForGuest($c, $c->stash->{project});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,8 @@ sub projectChain :Chained('/') :PathPart('project') :CaptureArgs(1) {
|
||||||
|
|
||||||
$c->stash->{project} = $c->model('DB::Projects')->find($projectName);
|
$c->stash->{project} = $c->model('DB::Projects')->find($projectName);
|
||||||
|
|
||||||
|
checkProjectVisibleForGuest($c, $c->stash->{project});
|
||||||
|
|
||||||
$c->stash->{isProjectOwner} = !$isCreate && isProjectOwner($c, $c->stash->{project});
|
$c->stash->{isProjectOwner} = !$isCreate && isProjectOwner($c, $c->stash->{project});
|
||||||
|
|
||||||
notFound($c, "Project ‘$projectName’ doesn't exist.")
|
notFound($c, "Project ‘$projectName’ doesn't exist.")
|
||||||
|
@ -161,6 +163,7 @@ sub updateProject {
|
||||||
, homepage => trim($c->stash->{params}->{homepage})
|
, homepage => trim($c->stash->{params}->{homepage})
|
||||||
, enabled => defined $c->stash->{params}->{enabled} ? 1 : 0
|
, enabled => defined $c->stash->{params}->{enabled} ? 1 : 0
|
||||||
, hidden => defined $c->stash->{params}->{visible} ? 0 : 1
|
, hidden => defined $c->stash->{params}->{visible} ? 0 : 1
|
||||||
|
, private => defined $c->stash->{params}->{private} ? 1 : 0
|
||||||
, owner => $owner
|
, owner => $owner
|
||||||
, enable_dynamic_run_command => $enable_dynamic_run_command
|
, enable_dynamic_run_command => $enable_dynamic_run_command
|
||||||
, declfile => trim($c->stash->{params}->{declarative}->{file})
|
, declfile => trim($c->stash->{params}->{declarative}->{file})
|
||||||
|
|
|
@ -109,7 +109,13 @@ sub deserialize :ActionClass('Deserialize') { }
|
||||||
sub index :Path :Args(0) {
|
sub index :Path :Args(0) {
|
||||||
my ($self, $c) = @_;
|
my ($self, $c) = @_;
|
||||||
$c->stash->{template} = 'overview.tt';
|
$c->stash->{template} = 'overview.tt';
|
||||||
$c->stash->{projects} = [$c->model('DB::Projects')->search({}, {order_by => ['enabled DESC', 'name']})];
|
|
||||||
|
my $includePrivate = $c->user_exists ? [1,0] : [0];
|
||||||
|
|
||||||
|
$c->stash->{projects} = [$c->model('DB::Projects')->search(
|
||||||
|
{private => {-in => $includePrivate}},
|
||||||
|
{order_by => ['enabled DESC', 'name']}
|
||||||
|
)];
|
||||||
$c->stash->{newsItems} = [$c->model('DB::NewsItems')->search({}, { order_by => ['createtime DESC'], rows => 5 })];
|
$c->stash->{newsItems} = [$c->model('DB::NewsItems')->search({}, { order_by => ['createtime DESC'], rows => 5 })];
|
||||||
$self->status_ok($c,
|
$self->status_ok($c,
|
||||||
entity => $c->stash->{projects}
|
entity => $c->stash->{projects}
|
||||||
|
@ -121,15 +127,23 @@ sub queue :Local :Args(0) :ActionClass('REST') { }
|
||||||
|
|
||||||
sub queue_GET {
|
sub queue_GET {
|
||||||
my ($self, $c) = @_;
|
my ($self, $c) = @_;
|
||||||
|
my $criteria = {finished => 0};
|
||||||
|
my $extra = {
|
||||||
|
columns => [@buildListColumns],
|
||||||
|
order_by => ["priority DESC", "id"]
|
||||||
|
};
|
||||||
|
unless ($c->user_exists) {
|
||||||
|
$criteria->{"project.private"} = 0;
|
||||||
|
$extra->{join} = {"jobset" => "project"};
|
||||||
|
}
|
||||||
$c->stash->{template} = 'queue.tt';
|
$c->stash->{template} = 'queue.tt';
|
||||||
$c->stash->{flashMsg} //= $c->flash->{buildMsg};
|
$c->stash->{flashMsg} //= $c->flash->{buildMsg};
|
||||||
$self->status_ok(
|
$self->status_ok(
|
||||||
$c,
|
$c,
|
||||||
entity => [$c->model('DB::Builds')->search(
|
entity => [$c->model('DB::Builds')->search(
|
||||||
{ finished => 0 },
|
$criteria,
|
||||||
{ order_by => ["globalpriority desc", "id"],
|
$extra
|
||||||
, columns => [@buildListColumns]
|
)]
|
||||||
})]
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,10 +152,15 @@ sub queue_summary :Local :Path('queue-summary') :Args(0) {
|
||||||
my ($self, $c) = @_;
|
my ($self, $c) = @_;
|
||||||
$c->stash->{template} = 'queue-summary.tt';
|
$c->stash->{template} = 'queue-summary.tt';
|
||||||
|
|
||||||
|
my $extra = " where ";
|
||||||
|
unless ($c->user_exists) {
|
||||||
|
$extra = "inner join Projects p on p.name = project where p.private = 0 and ";
|
||||||
|
}
|
||||||
|
|
||||||
$c->stash->{queued} = dbh($c)->selectall_arrayref(
|
$c->stash->{queued} = dbh($c)->selectall_arrayref(
|
||||||
"select jobsets.project as project, jobsets.name as jobset, count(*) as queued, min(timestamp) as oldest, max(timestamp) as newest from Builds " .
|
"select jobsets.project as project, jobsets.name as jobset, count(*) as queued, min(timestamp) as oldest, max(timestamp) as newest from Builds " .
|
||||||
"join Jobsets jobsets on jobsets.id = builds.jobset_id " .
|
"join Jobsets jobsets on jobsets.id = builds.jobset_id " .
|
||||||
"where finished = 0 group by jobsets.project, jobsets.name order by queued desc",
|
"$extra finished = 0 group by jobsets.project, jobsets.name order by queued desc",
|
||||||
{ Slice => {} });
|
{ Slice => {} });
|
||||||
|
|
||||||
$c->stash->{systems} = dbh($c)->selectall_arrayref(
|
$c->stash->{systems} = dbh($c)->selectall_arrayref(
|
||||||
|
@ -154,12 +173,19 @@ sub status :Local :Args(0) :ActionClass('REST') { }
|
||||||
|
|
||||||
sub status_GET {
|
sub status_GET {
|
||||||
my ($self, $c) = @_;
|
my ($self, $c) = @_;
|
||||||
|
my $criteria = { "buildsteps.busy" => { '!=', 0 } };
|
||||||
|
my $join = ["buildsteps"];
|
||||||
|
unless ($c->user_exists) {
|
||||||
|
$criteria->{"project.private"} = 0;
|
||||||
|
push @{$join}, {"jobset" => "project"};
|
||||||
|
}
|
||||||
|
|
||||||
$self->status_ok(
|
$self->status_ok(
|
||||||
$c,
|
$c,
|
||||||
entity => [$c->model('DB::Builds')->search(
|
entity => [$c->model('DB::Builds')->search(
|
||||||
{ "buildsteps.busy" => { '!=', 0 } },
|
$criteria,
|
||||||
{ order_by => ["globalpriority DESC", "id"],
|
{ order_by => ["globalpriority DESC", "id"],
|
||||||
join => "buildsteps",
|
join => $join,
|
||||||
columns => [@buildListColumns, 'buildsteps.drvpath', 'buildsteps.type']
|
columns => [@buildListColumns, 'buildsteps.drvpath', 'buildsteps.type']
|
||||||
})]
|
})]
|
||||||
);
|
);
|
||||||
|
@ -201,13 +227,18 @@ sub machines :Local Args(0) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $extra = "where";
|
||||||
|
unless ($c->user_exists) {
|
||||||
|
$extra = "inner join Projects p on p.name = jobsets.project where p.private = 0 and ";
|
||||||
|
}
|
||||||
|
|
||||||
$c->stash->{machines} = $machines;
|
$c->stash->{machines} = $machines;
|
||||||
$c->stash->{steps} = dbh($c)->selectall_arrayref(
|
$c->stash->{steps} = dbh($c)->selectall_arrayref(
|
||||||
"select build, stepnr, s.system as system, s.drvpath as drvpath, machine, s.starttime as starttime, jobsets.project as project, jobsets.name as jobset, job, s.busy as busy " .
|
"select build, stepnr, s.system as system, s.drvpath as drvpath, machine, s.starttime as starttime, jobsets.project as project, jobsets.name as jobset, job, s.busy as busy " .
|
||||||
"from BuildSteps s " .
|
"from BuildSteps s " .
|
||||||
"join Builds b on s.build = b.id " .
|
"join Builds b on s.build = b.id " .
|
||||||
"join Jobsets jobsets on jobsets.id = b.jobset_id " .
|
"join Jobsets jobsets on jobsets.id = b.jobset_id " .
|
||||||
"where busy != 0 order by machine, stepnr",
|
"$extra busy != 0 order by machine, stepnr",
|
||||||
{ Slice => {} });
|
{ Slice => {} });
|
||||||
$c->stash->{template} = 'machine-status.tt';
|
$c->stash->{template} = 'machine-status.tt';
|
||||||
$self->status_ok($c, entity => $c->stash->{machines});
|
$self->status_ok($c, entity => $c->stash->{machines});
|
||||||
|
@ -449,16 +480,28 @@ sub steps :Local Args(0) {
|
||||||
|
|
||||||
my $resultsPerPage = 20;
|
my $resultsPerPage = 20;
|
||||||
|
|
||||||
|
my $criteria = {
|
||||||
|
"me.starttime" => { '!=', undef },
|
||||||
|
"me.stoptime" => { '!=', undef }
|
||||||
|
};
|
||||||
|
|
||||||
|
my $extra = {
|
||||||
|
order_by => [ "me.stoptime desc" ],
|
||||||
|
rows => $resultsPerPage,
|
||||||
|
offset => ($page - 1) * $resultsPerPage,
|
||||||
|
};
|
||||||
|
|
||||||
|
unless ($c->user_exists) {
|
||||||
|
$criteria->{"project.private"} = 0;
|
||||||
|
$extra->{join} = [{"build" => {"jobset" => "project"}}];
|
||||||
|
}
|
||||||
|
|
||||||
$c->stash->{page} = $page;
|
$c->stash->{page} = $page;
|
||||||
$c->stash->{resultsPerPage} = $resultsPerPage;
|
$c->stash->{resultsPerPage} = $resultsPerPage;
|
||||||
$c->stash->{steps} = [ $c->model('DB::BuildSteps')->search(
|
$c->stash->{steps} = [ $c->model('DB::BuildSteps')->search(
|
||||||
{ starttime => { '!=', undef },
|
$criteria,
|
||||||
stoptime => { '!=', undef }
|
$extra
|
||||||
},
|
) ];
|
||||||
{ order_by => [ "stoptime desc" ],
|
|
||||||
rows => $resultsPerPage,
|
|
||||||
offset => ($page - 1) * $resultsPerPage
|
|
||||||
}) ];
|
|
||||||
|
|
||||||
$c->stash->{total} = approxTableSize($c, "IndexBuildStepsOnStopTime");
|
$c->stash->{total} = approxTableSize($c, "IndexBuildStepsOnStopTime");
|
||||||
}
|
}
|
||||||
|
@ -479,28 +522,58 @@ sub search :Local Args(0) {
|
||||||
|
|
||||||
$c->model('DB')->schema->txn_do(sub {
|
$c->model('DB')->schema->txn_do(sub {
|
||||||
$c->model('DB')->schema->storage->dbh->do("SET LOCAL statement_timeout = 20000");
|
$c->model('DB')->schema->storage->dbh->do("SET LOCAL statement_timeout = 20000");
|
||||||
$c->stash->{projects} = [ $c->model('DB::Projects')->search(
|
|
||||||
{ -and =>
|
my $projectCriteria = {
|
||||||
|
-and =>
|
||||||
[ { -or => [ name => { ilike => "%$query%" }, displayName => { ilike => "%$query%" }, description => { ilike => "%$query%" } ] }
|
[ { -or => [ name => { ilike => "%$query%" }, displayName => { ilike => "%$query%" }, description => { ilike => "%$query%" } ] }
|
||||||
, { hidden => 0 }
|
, { hidden => 0 }
|
||||||
]
|
]
|
||||||
},
|
};
|
||||||
{ order_by => ["name"] } ) ];
|
|
||||||
|
|
||||||
$c->stash->{jobsets} = [ $c->model('DB::Jobsets')->search(
|
my $jobsetCriteria = {
|
||||||
{ -and =>
|
-and =>
|
||||||
[ { -or => [ "me.name" => { ilike => "%$query%" }, "me.description" => { ilike => "%$query%" } ] }
|
[ { -or => [ "me.name" => { ilike => "%$query%" }, "me.description" => { ilike => "%$query%" } ] }
|
||||||
, { "project.hidden" => 0, "me.hidden" => 0 }
|
, { "project.hidden" => 0, "me.hidden" => 0 }
|
||||||
]
|
]
|
||||||
},
|
};
|
||||||
{ order_by => ["project", "name"], join => ["project"] } ) ];
|
|
||||||
|
|
||||||
$c->stash->{jobs} = [ $c->model('DB::Builds')->search(
|
my $buildCriteria = {
|
||||||
{ "job" => { ilike => "%$query%" }
|
"job" => { ilike => "%$query%" }
|
||||||
, "project.hidden" => 0
|
, "project.hidden" => 0
|
||||||
, "jobset.hidden" => 0
|
, "jobset.hidden" => 0
|
||||||
, iscurrent => 1
|
, iscurrent => 1
|
||||||
},
|
};
|
||||||
|
|
||||||
|
my $buildSearchExtra = {
|
||||||
|
order_by => ["id desc"]
|
||||||
|
, rows => $c->stash->{limit}, join => []
|
||||||
|
};
|
||||||
|
|
||||||
|
my $outCriteria = {
|
||||||
|
"buildoutputs.path" => { ilike => "%$query%" }
|
||||||
|
};
|
||||||
|
|
||||||
|
my $drvCriteria = { "drvpath" => { ilike => "%$query%" } };
|
||||||
|
|
||||||
|
unless ($c->user_exists) {
|
||||||
|
$projectCriteria->{private} = 0;
|
||||||
|
$jobsetCriteria->{"project.private"} = 0;
|
||||||
|
$buildCriteria->{"project.private"} = 0;
|
||||||
|
push @{$buildSearchExtra->{join}}, {"jobset" => "project"};
|
||||||
|
$outCriteria->{"project.private"} = 0;
|
||||||
|
$drvCriteria->{"project.private"} = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$c->stash->{projects} = [ $c->model('DB::Projects')->search(
|
||||||
|
$projectCriteria,
|
||||||
|
{ order_by => ["name"] } ) ];
|
||||||
|
|
||||||
|
$c->stash->{jobsets} = [ $c->model('DB::Jobsets')->search(
|
||||||
|
$jobsetCriteria,
|
||||||
|
{ order_by => ["project", "name"], join => ["project"] } ) ];
|
||||||
|
|
||||||
|
$c->stash->{jobs} = [ $c->model('DB::Builds')->search(
|
||||||
|
$buildCriteria,
|
||||||
{
|
{
|
||||||
order_by => ["jobset.project", "jobset.name", "job"],
|
order_by => ["jobset.project", "jobset.name", "job"],
|
||||||
join => { "jobset" => "project" },
|
join => { "jobset" => "project" },
|
||||||
|
@ -509,17 +582,16 @@ sub search :Local Args(0) {
|
||||||
];
|
];
|
||||||
|
|
||||||
# Perform build search in separate queries to prevent seq scan on buildoutputs table.
|
# Perform build search in separate queries to prevent seq scan on buildoutputs table.
|
||||||
|
my $outExtra = $buildSearchExtra;
|
||||||
|
push @{$outExtra->{join}}, "buildoutputs";
|
||||||
$c->stash->{builds} = [ $c->model('DB::Builds')->search(
|
$c->stash->{builds} = [ $c->model('DB::Builds')->search(
|
||||||
{ "buildoutputs.path" => { ilike => "%$query%" } },
|
$outCriteria,
|
||||||
{ order_by => ["id desc"], join => ["buildoutputs"]
|
$outExtra
|
||||||
, rows => $c->stash->{limit}
|
) ];
|
||||||
} ) ];
|
|
||||||
|
|
||||||
$c->stash->{buildsdrv} = [ $c->model('DB::Builds')->search(
|
$c->stash->{buildsdrv} = [ $c->model('DB::Builds')->search(
|
||||||
{ "drvpath" => { ilike => "%$query%" } },
|
$drvCriteria,
|
||||||
{ order_by => ["id desc"]
|
$buildSearchExtra ) ];
|
||||||
, rows => $c->stash->{limit}
|
|
||||||
} ) ];
|
|
||||||
|
|
||||||
$c->stash->{resource} = { projects => $c->stash->{projects},
|
$c->stash->{resource} = { projects => $c->stash->{projects},
|
||||||
jobsets => $c->stash->{jobsets},
|
jobsets => $c->stash->{jobsets},
|
||||||
|
|
|
@ -29,6 +29,7 @@ our @EXPORT = qw(
|
||||||
approxTableSize
|
approxTableSize
|
||||||
requireLocalStore
|
requireLocalStore
|
||||||
dbh
|
dbh
|
||||||
|
checkProjectVisibleForGuest
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -256,6 +257,14 @@ sub requireProjectOwner {
|
||||||
unless isProjectOwner($c, $project);
|
unless isProjectOwner($c, $project);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub checkProjectVisibleForGuest {
|
||||||
|
my ($c, $project) = @_;
|
||||||
|
if (defined $project && $project->private == 1 && !$c->user_exists) {
|
||||||
|
my $projectName = $project->name;
|
||||||
|
notFound($c, "Project ‘$projectName’ not found!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub isAdmin {
|
sub isAdmin {
|
||||||
my ($c) = @_;
|
my ($c) = @_;
|
||||||
|
|
|
@ -182,17 +182,34 @@ sub findLog {
|
||||||
my ($c, $drvPath, @outPaths) = @_;
|
my ($c, $drvPath, @outPaths) = @_;
|
||||||
|
|
||||||
if (defined $drvPath) {
|
if (defined $drvPath) {
|
||||||
|
unless ($c->user_exists) {
|
||||||
|
my $existsForGuest = $c->model('DB::BuildSteps')->search(
|
||||||
|
{"me.drvpath" => $drvPath, "project.private" => 0},
|
||||||
|
{join => {build => {"jobset" => "project"}}}
|
||||||
|
);
|
||||||
|
if ($existsForGuest == 0) {
|
||||||
|
notFound($c, "Resource not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
my $logPath = getDrvLogPath($drvPath);
|
my $logPath = getDrvLogPath($drvPath);
|
||||||
return $logPath if defined $logPath;
|
return $logPath if defined $logPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
return undef if scalar @outPaths == 0;
|
return undef if scalar @outPaths == 0;
|
||||||
|
|
||||||
|
my $join = ["buildstepoutputs"];
|
||||||
|
my $criteria = { path => { -in => [@outPaths] } };
|
||||||
|
unless ($c->user_exists) {
|
||||||
|
push @{$join}, {"build" => {jobset => "project"}};
|
||||||
|
$criteria->{"project.private"} = 0;
|
||||||
|
}
|
||||||
|
|
||||||
my @steps = $c->model('DB::BuildSteps')->search(
|
my @steps = $c->model('DB::BuildSteps')->search(
|
||||||
{ path => { -in => [@outPaths] } },
|
$criteria,
|
||||||
{ select => ["drvpath"]
|
{ select => ["drvpath"]
|
||||||
, distinct => 1
|
, distinct => 1
|
||||||
, join => "buildstepoutputs"
|
, join => $join
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach my $step (@steps) {
|
foreach my $step (@steps) {
|
||||||
|
@ -285,9 +302,19 @@ sub getEvals {
|
||||||
|
|
||||||
my $me = $evals_result_set->current_source_alias;
|
my $me = $evals_result_set->current_source_alias;
|
||||||
|
|
||||||
my @evals = $evals_result_set->search(
|
my $criteria = { hasnewbuilds => 1 };
|
||||||
{ hasnewbuilds => 1 },
|
my $extra = {
|
||||||
{ order_by => "$me.id DESC", rows => $rows, offset => $offset });
|
order_by => "$me.id DESC",
|
||||||
|
rows => $rows,
|
||||||
|
offset => $offset
|
||||||
|
};
|
||||||
|
unless ($c->user_exists) {
|
||||||
|
$extra->{join} = {"jobset" => "project"};
|
||||||
|
$criteria->{"project.private"} = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
my @evals = $evals_result_set->search($criteria, $extra);
|
||||||
|
|
||||||
my @res = ();
|
my @res = ();
|
||||||
my $cache = {};
|
my $cache = {};
|
||||||
|
|
||||||
|
@ -412,8 +439,7 @@ sub readIntoSocket{
|
||||||
my $sock;
|
my $sock;
|
||||||
|
|
||||||
eval {
|
eval {
|
||||||
my $x= join(" ", @{$args{cmd}});
|
open($sock, "-|", @{$args{cmd}}) or die q(failed to open socket from command:\n $x);
|
||||||
open($sock, "-|", $x) or die q(failed to open socket from command:\n $x);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return $sock;
|
return $sock;
|
||||||
|
|
|
@ -62,6 +62,12 @@ __PACKAGE__->table("projects");
|
||||||
default_value: 0
|
default_value: 0
|
||||||
is_nullable: 0
|
is_nullable: 0
|
||||||
|
|
||||||
|
=head2 private
|
||||||
|
|
||||||
|
data_type: 'integer'
|
||||||
|
default_value: 0
|
||||||
|
is_nullable: 0
|
||||||
|
|
||||||
=head2 owner
|
=head2 owner
|
||||||
|
|
||||||
data_type: 'text'
|
data_type: 'text'
|
||||||
|
@ -107,6 +113,8 @@ __PACKAGE__->add_columns(
|
||||||
{ data_type => "integer", default_value => 1, is_nullable => 0 },
|
{ data_type => "integer", default_value => 1, is_nullable => 0 },
|
||||||
"hidden",
|
"hidden",
|
||||||
{ data_type => "integer", default_value => 0, is_nullable => 0 },
|
{ data_type => "integer", default_value => 0, is_nullable => 0 },
|
||||||
|
"private",
|
||||||
|
{ data_type => "integer", default_value => 0, is_nullable => 0 },
|
||||||
"owner",
|
"owner",
|
||||||
{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },
|
{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },
|
||||||
"homepage",
|
"homepage",
|
||||||
|
@ -236,8 +244,8 @@ Composing rels: L</projectmembers> -> username
|
||||||
__PACKAGE__->many_to_many("usernames", "projectmembers", "username");
|
__PACKAGE__->many_to_many("usernames", "projectmembers", "username");
|
||||||
|
|
||||||
|
|
||||||
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2022-01-24 14:20:32
|
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2022-11-22 12:51:02
|
||||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:PtXDyT8Pc7LYhhdEG39EKQ
|
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ppyLpFU2fZASFANhD7vUgg
|
||||||
|
|
||||||
use JSON::MaybeXS;
|
use JSON::MaybeXS;
|
||||||
|
|
||||||
|
@ -267,6 +275,7 @@ sub as_json {
|
||||||
"enabled" => $self->get_column("enabled") ? JSON::MaybeXS::true : JSON::MaybeXS::false,
|
"enabled" => $self->get_column("enabled") ? JSON::MaybeXS::true : JSON::MaybeXS::false,
|
||||||
"enable_dynamic_run_command" => $self->get_column("enable_dynamic_run_command") ? JSON::MaybeXS::true : JSON::MaybeXS::false,
|
"enable_dynamic_run_command" => $self->get_column("enable_dynamic_run_command") ? JSON::MaybeXS::true : JSON::MaybeXS::false,
|
||||||
"hidden" => $self->get_column("hidden") ? JSON::MaybeXS::true : JSON::MaybeXS::false,
|
"hidden" => $self->get_column("hidden") ? JSON::MaybeXS::true : JSON::MaybeXS::false,
|
||||||
|
"private" => $self->get_column("private") ? JSON::MaybeXS::true : JSON::MaybeXS::false,
|
||||||
|
|
||||||
"jobsets" => [ map { $_->name } $self->jobsets ]
|
"jobsets" => [ map { $_->name } $self->jobsets ]
|
||||||
);
|
);
|
||||||
|
|
|
@ -42,7 +42,15 @@
|
||||||
[% END %]
|
[% END %]
|
||||||
|
|
||||||
[% BLOCK renderJobsetInputs %]
|
[% BLOCK renderJobsetInputs %]
|
||||||
<table class="table table-striped table-condensed show-on-legacy">
|
<div class="card show-on-flake border-danger">
|
||||||
|
<div class="text-danger card-body">
|
||||||
|
<h5 class="card-title">Jobset Inputs don't take any effect for flakes</h5>
|
||||||
|
<p class="card-text">
|
||||||
|
These are only available to configure Hydra plugins.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped table-condensed">
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th></th><th>Input name</th><th>Type</th><th style="width: 50%">Value</th><th>Notify committers</th></tr>
|
<tr><th></th><th>Input name</th><th>Type</th><th style="width: 50%">Value</th><th>Notify committers</th></tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
|
@ -17,6 +17,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-sm-3" for="editprojectprivate">Private</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="checkbox" id="editprojectprivate" name="private" [% IF project.private %] checked="checked" [% END %]/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-3" for="editprojectidentifier">Identifier</label>
|
<label class="col-sm-3" for="editprojectidentifier">Identifier</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
[% FOREACH p IN projects %]
|
[% FOREACH p IN projects %]
|
||||||
<tr class="project [% IF !p.enabled %]disabled-project[% END %]">
|
<tr class="project [% IF !p.enabled %]disabled-project[% END %]">
|
||||||
<td><span class="[% IF !p.enabled %]disabled-project[% END %] [%+ IF p.hidden %]hidden-project[% END %]">[% INCLUDE renderProjectName project=p.name inRow=1 %]</span></td>
|
<td>[% IF p.private %]🔒[% END %] <span class="[% IF !p.enabled %]disabled-project[% END %] [%+ IF p.hidden %]hidden-project[% END %]">[% INCLUDE renderProjectName project=p.name inRow=1 %]</span></td>
|
||||||
<td>[% HTML.escape(p.displayname) %]</td>
|
<td>[% HTML.escape(p.displayname) %]</td>
|
||||||
<td>[% WRAPPER maybeLink uri=p.homepage %][% HTML.escape(p.description) %][% END %]</td>
|
<td>[% WRAPPER maybeLink uri=p.homepage %][% HTML.escape(p.description) %][% END %]</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
[% WRAPPER layout.tt title="Project $project.name" %]
|
[% IF project.private %]
|
||||||
|
[% lock = ' 🔒' %]
|
||||||
|
[% ELSE %]
|
||||||
|
[% lock = '' %]
|
||||||
|
[% END %]
|
||||||
|
[% WRAPPER layout.tt titleHTML="Project $project.name$lock" title="Project $project.name" %]
|
||||||
[% PROCESS common.tt %]
|
[% PROCESS common.tt %]
|
||||||
|
|
||||||
<ul class="nav nav-tabs">
|
<ul class="nav nav-tabs">
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
-- add a map of the lowercase name of your table to the CamelCase
|
-- add a map of the lowercase name of your table to the CamelCase
|
||||||
-- version of your table.
|
-- version of your table.
|
||||||
--
|
--
|
||||||
-- 3. Run `make -C src/sql update-dbix` in the root
|
-- 3. Run `just update-dbix` in the root
|
||||||
-- of the project directory, and git add / git commit the changed,
|
-- of the project directory, and git add / git commit the changed,
|
||||||
-- generated files.
|
-- generated files.
|
||||||
--
|
--
|
||||||
|
@ -44,6 +44,7 @@ create table Projects (
|
||||||
description text,
|
description text,
|
||||||
enabled integer not null default 1,
|
enabled integer not null default 1,
|
||||||
hidden integer not null default 0,
|
hidden integer not null default 0,
|
||||||
|
private integer not null default 0,
|
||||||
owner text not null,
|
owner text not null,
|
||||||
homepage text, -- URL for the project
|
homepage text, -- URL for the project
|
||||||
declfile text, -- File containing declarative jobset specification
|
declfile text, -- File containing declarative jobset specification
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
-- Records of RunCommand executions
|
-- Records of RunCommand executions
|
||||||
--
|
--
|
||||||
-- The intended flow is:
|
-- The intended flow is:
|
||||||
|
|
|
@ -51,7 +51,8 @@ subtest "Read project 'tests'" => sub {
|
||||||
homepage => "",
|
homepage => "",
|
||||||
jobsets => [],
|
jobsets => [],
|
||||||
name => "tests",
|
name => "tests",
|
||||||
owner => "root"
|
owner => "root",
|
||||||
|
"private" => JSON::MaybeXS::false
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -96,7 +97,8 @@ subtest "Transitioning from declarative project to normal" => sub {
|
||||||
file => "bogus",
|
file => "bogus",
|
||||||
type => "boolean",
|
type => "boolean",
|
||||||
value => "false"
|
value => "false"
|
||||||
}
|
},
|
||||||
|
"private" => JSON::MaybeXS::false
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -135,7 +137,8 @@ subtest "Transitioning from declarative project to normal" => sub {
|
||||||
homepage => "",
|
homepage => "",
|
||||||
jobsets => [],
|
jobsets => [],
|
||||||
name => "tests",
|
name => "tests",
|
||||||
owner => "root"
|
owner => "root",
|
||||||
|
"private" => JSON::MaybeXS::false
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
168
t/private-projects.t
Normal file
168
t/private-projects.t
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Setup;
|
||||||
|
use Test2::V0;
|
||||||
|
use HTTP::Request::Common;
|
||||||
|
use HTML::TreeBuilder::XPath;
|
||||||
|
use JSON::MaybeXS;
|
||||||
|
|
||||||
|
my %ctx = test_init(
|
||||||
|
use_external_destination_store => 0
|
||||||
|
);
|
||||||
|
|
||||||
|
require Hydra::Schema;
|
||||||
|
require Hydra::Model::DB;
|
||||||
|
|
||||||
|
require Catalyst::Test;
|
||||||
|
Catalyst::Test->import('Hydra');
|
||||||
|
my $db = Hydra::Model::DB->new;
|
||||||
|
hydra_setup($db);
|
||||||
|
|
||||||
|
my $scratch = "$ctx{tmpdir}/scratch";
|
||||||
|
mkdir $scratch;
|
||||||
|
|
||||||
|
my $uri = "file://$scratch/git-repo";
|
||||||
|
my $jobset = createJobsetWithOneInput('gitea', 'git-input.nix', 'src', 'git', $uri, $ctx{jobsdir});
|
||||||
|
|
||||||
|
ok(request('/project/tests')->is_success, "Project 'tests' exists");
|
||||||
|
my $project = $db->resultset('Projects')->find({name => "tests"})->update({private => 1});
|
||||||
|
ok(
|
||||||
|
!request('/project/tests')->is_success,
|
||||||
|
"Project 'tests' is private now and should be unreachable"
|
||||||
|
);
|
||||||
|
|
||||||
|
my $user = $db->resultset('Users')->create({
|
||||||
|
username => "testing",
|
||||||
|
emailaddress => 'testing@invalid.org',
|
||||||
|
password => ''
|
||||||
|
});
|
||||||
|
$user->setPassword('foobar');
|
||||||
|
|
||||||
|
my $auth = request(
|
||||||
|
POST(
|
||||||
|
'/login',
|
||||||
|
{username => 'testing', 'password' => 'foobar'},
|
||||||
|
Origin => 'http://localhost', Accept => 'application/json'
|
||||||
|
),
|
||||||
|
{host => 'localhost'}
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(
|
||||||
|
$auth->code == 302,
|
||||||
|
"Successfully logged in"
|
||||||
|
);
|
||||||
|
|
||||||
|
my $cookie = (split /;/, $auth->header('set_cookie'))[0];
|
||||||
|
|
||||||
|
ok(
|
||||||
|
request(GET(
|
||||||
|
'/project/tests',
|
||||||
|
Cookie => $cookie
|
||||||
|
))->is_success,
|
||||||
|
"Project visible for authenticated user."
|
||||||
|
);
|
||||||
|
|
||||||
|
updateRepository('gitea', "$ctx{testdir}/jobs/git-update.sh", $scratch);
|
||||||
|
|
||||||
|
ok(evalSucceeds($jobset), "Evaluating nix expression");
|
||||||
|
is(nrQueuedBuildsForJobset($jobset), 1, "Evaluating jobs/runcommand.nix should result in 1 build1");
|
||||||
|
|
||||||
|
ok(
|
||||||
|
request('/eval/1')->code == 404,
|
||||||
|
'Eval of private project not available for unauthenticated user.'
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(
|
||||||
|
request(GET '/eval/1', Cookie => $cookie)->is_success,
|
||||||
|
'Eval available for authenticated User'
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(
|
||||||
|
request(GET '/jobset/tests/gitea', Cookie => $cookie)->is_success,
|
||||||
|
'Jobset available for user'
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(
|
||||||
|
request(GET '/jobset/tests/gitea')->code == 404,
|
||||||
|
'Jobset unavailable for guest'
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(
|
||||||
|
request('/build/1')->code == 404,
|
||||||
|
'Build of private project not available for unauthenticated user.'
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(
|
||||||
|
request(GET '/build/1', Cookie => $cookie)->is_success,
|
||||||
|
'Build available for authenticated User'
|
||||||
|
);
|
||||||
|
|
||||||
|
(my $build) = queuedBuildsForJobset($jobset);
|
||||||
|
ok(runBuild($build), "Build should succeed with exit code 0");
|
||||||
|
|
||||||
|
ok(
|
||||||
|
request(GET '/jobset/tests/gitea/channel/latest', Cookie => $cookie)->is_success,
|
||||||
|
'Channel available for authenticated user'
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(
|
||||||
|
request(GET '/jobset/tests/gitea/channel/latest')->code == 404,
|
||||||
|
'Channel unavailable for guest'
|
||||||
|
);
|
||||||
|
|
||||||
|
updateRepository('gitea', "$ctx{testdir}/jobs/git-update.sh", $scratch);
|
||||||
|
ok(evalSucceeds($jobset), "Evaluating nix expression");
|
||||||
|
|
||||||
|
my $latest_builds_unauth = request(GET "/all");
|
||||||
|
|
||||||
|
my $tree = HTML::TreeBuilder::XPath->new;
|
||||||
|
$tree->parse($latest_builds_unauth->content);
|
||||||
|
ok(!$tree->exists('/html//tbody/tr'), "No builds available");
|
||||||
|
|
||||||
|
my $latest_builds = request(GET "/all", Cookie => $cookie);
|
||||||
|
|
||||||
|
$tree = HTML::TreeBuilder::XPath->new;
|
||||||
|
$tree->parse($latest_builds->content);
|
||||||
|
ok($tree->exists('/html//tbody/tr'), "Builds available");
|
||||||
|
|
||||||
|
my $p2 = $db->resultset("Projects")->create({name => "public", displayname => "public", owner => "root"});
|
||||||
|
my $jobset2 = $p2->jobsets->create({
|
||||||
|
name => "public", nixexprpath => 'basic.nix', nixexprinput => "jobs", emailoverride => ""
|
||||||
|
});
|
||||||
|
|
||||||
|
my $jobsetinput = $jobset2->jobsetinputs->create({name => "jobs", type => "path"});
|
||||||
|
$jobsetinput->jobsetinputalts->create({altnr => 0, value => $ctx{jobsdir}});
|
||||||
|
|
||||||
|
updateRepository('gitea', "$ctx{testdir}/jobs/git-update.sh", $scratch);
|
||||||
|
ok(evalSucceeds($jobset2), "Evaluating nix expression");
|
||||||
|
is(
|
||||||
|
nrQueuedBuildsForJobset($jobset2),
|
||||||
|
3,
|
||||||
|
"Evaluating jobs/runcommand.nix should result in 3 builds"
|
||||||
|
);
|
||||||
|
|
||||||
|
(my $b1, my $b2, my $b3) = queuedBuildsForJobset($jobset2);
|
||||||
|
ok(runBuild($b1), "Build should succeed with exit code 0");
|
||||||
|
ok(runBuild($b2), "Build should succeed with exit code 0");
|
||||||
|
ok(runBuild($b3), "Build should succeed with exit code 0");
|
||||||
|
my $latest_builds_unauth2 = request(GET "/all");
|
||||||
|
|
||||||
|
$tree = HTML::TreeBuilder::XPath->new;
|
||||||
|
$tree->parse($latest_builds_unauth2->content);
|
||||||
|
is(
|
||||||
|
scalar $tree->findvalues('/html//tbody/tr'),
|
||||||
|
3,
|
||||||
|
"Three builds available"
|
||||||
|
);
|
||||||
|
|
||||||
|
my $latest_builds2 = request(GET "/all", Cookie => $cookie);
|
||||||
|
|
||||||
|
$tree = HTML::TreeBuilder::XPath->new;
|
||||||
|
$tree->parse($latest_builds2->content);
|
||||||
|
is(
|
||||||
|
scalar $tree->findvalues('/html//tbody/tr'),
|
||||||
|
4,
|
||||||
|
"Three builds available"
|
||||||
|
);
|
||||||
|
|
||||||
|
done_testing;
|
Loading…
Reference in a new issue