Best practice to react on commits

I would like to run a script on Phabricator for each commit.
Is there a way to listen for all commits?

Depending on what you are looking to do, you may be able to accomplish it with a Herald rule. One of the options for a Herald trigger is when new commits appear in tracked repositories. If you navigate to /herald/create, one of the options is:

Let me know if that helps :slight_smile:

Hi @josh. Thanks for the answer. Unfortunately, you can not trigger cmds with a heral rule. And i don’t thing It will be ever possible as it is a security issue.
I was looking more in a way to listen to new commits using an api, or if it possible with a script from the phabricator bin folder.

Something simple;
for ex. for every commit print the hash and the repository url.

This isn’t entirely true. One action you can trigger with a Herald rule is “Run build plans” (only available on the global trigger for commits). You could set up a build plan that uses whichever machine you want (including the local phabricator machine) and runs any arbitrary commands. See the Harbormaster User Guide for more info on how to do that. You may also need to reference the Drydock documentation if you go this route.

It depends on your specific use-case, but if the harbormaster option doesn’t seem quite right you have a few different options of varying difficulty and flexibility that are discussed in this ticket.

I followed the documentation and I created:

  • an Almanac Device (with the host name)
  • an Almanac Service - only for binding the host
  • an `Almanac Network`` - for nothing
  • an Blueprint type Almanac Hosts with the Almanac Service and credentials
  • a Harbourmaster build plan with:
  • a Draydock: Run Command with a command;

but the drydock it’s not asking for the Blueprint, instead is asking of Drydock Lease
How do I create a Drydock Lease?

The first step of your build plan should be “Lease Working Copy”. That step takes a Blueprint as a parameter and produces a Drydock Lease which can be used in subsequent build steps. The name you specify for “Artifact Name” in the Lease Working Copy step is what you will use in the “Drydock Lease” input of the Run Command step (as shown in the screenshots below):

1 Like

Thanks for the info.
Now the plan looks like this:

But the build is failing with the log message:

exception 'PhabricatorWorkerPermanentFailureException' with message 'Lease "PHID-DRYL-    fogf4bv6h55eh2iuyi35" never activated.' in /var/www/phabricator/src/applications/harbormaster/step/HarbormasterLeaseWorkingCopyBuildStepImplementation.php:91
2	Stack trace:
3	#0 /var/www/phabricator/src/applications/harbormaster/worker/HarbormasterTargetWorker.php(64): HarbormasterLeaseWorkingCopyBuildStepImplementation->execute(Object(HarbormasterBuild), Object(HarbormasterBuildTarget))
4	#1 /var/www/phabricator/src/infrastructure/daemon/workers/PhabricatorWorker.php(123): HarbormasterTargetWorker->doWork()
5	#2 /var/www/phabricator/src/infrastructure/daemon/workers/storage/PhabricatorWorkerActiveTask.php(171): PhabricatorWorker->executeTask()
6	#3 /var/www/phabricator/src/infrastructure/daemon/workers/PhabricatorTaskmasterDaemon.php(22): PhabricatorWorkerActiveTask->executeTask()
7	#4 /var/www/libphutil/src/daemon/PhutilDaemon.php(219): PhabricatorTaskmasterDaemon->run()
8	#5 /var/www/libphutil/scripts/daemon/exec/exec_daemon.php(131): PhutilDaemon->execute()
9	#6 {main}

Lease log:

How can I activate the Drydock blueprint?

That error can happen for a number of reasons. Based on what you’ve posted, I’d check on the blueprint’s page and make sure the build plan is authorized to use that blueprint. You may have to click on the authorization and approve it.

The blueprint which I am using is authorized.
If I run the drydock in the cmd line:

./bin/drydock lease --type host

then it works, but if I run the build manually, it fails.
Same issue as in this ticket

Any idea?

Ok. The user which reported this as an issue have a solution:

But the problem is that the Blueprint working copy is still failing with the error message.

Resource activation failed: [CommandException] Command failed with error #255! COMMAND ssh '-o' 'LogLevel=quiet' '-o' 'StrictHostKeyChecking=no' '-o' 'UserKnownHostsFile=/dev/null' '-o' 'BatchMode=yes' -l '********' -p '22' -i '********' '' -- 'git clone -- '\''ssh://********@phabricator.bla.bla/source/dev_sandbox.git'\'' '\''/var/drydock/workingcopy-107/repo/dev_sandbox/'\''' STDOUT (empty) STDERR (empty)

Before debugging this error, from what I see from the error log message, the Blueprint is trying to close the repository locally on that host. But I don’t want to clone a repository on the host. I just want to run a simple command on the host for each commit.

I am just wandering if it’s not an easier way of doing this…

You could write a custom herald action or maybe customize the phabricator post receive hooks.

What exactly are you trying to achieve, @Avram_Remus? Harbormaster should not be able to execute commands on the “master” phabricator hosts, for security reasons, but if we know what you’re aiming for, there might be an easier way.

Thanks @avivey for asking.
We can run the cmd also on another machine.
Our workflow is like this:

  1. we have 300+ repositories
  2. developers have a local checkout of the repositories which they are working on
  3. ones a developer is pushing the code, we need to make it available instant for the production
  4. we have a global checkout of all the repositories where for Production it’s read only.
  5. Only one machine has read write access in order to pull into the repositories.

It is quite difficult to configure this using Harbourmaster.
We managed it like this:

  1. herald rule which listen for all the commits and run a build plan
  2. the build plan has a step which call a jenkins job with the callsign of the repository
  3. jenkins pull the new commits in the repository with the respective callsign in a new tread

At the moment it works. But I think having this done with a Harbourmaster build is much nicer as every build is running independent where a jenkins job can run one time at the time.
In this case we can not wait until jenkins is finishing building and pass the result to Phabricator.

The only think what I don’t understand is why there are so many steps to be done in order to run a simple cmd on a machine.

Ok, I think I understand better now.

Roughly, if you’re using Jenkins, you don’t need any of the Drydock and Almanac stuff - those are basically there as an alternative to Jenkins. You only need the HTTP Call step, which invokes Jenkins.

You could also have a different service replace Jenkins for this task only, so it’s running on the right machine and not on Jenkins.
See also

Phabricator also has a Mirroring ability, so you can configure an SSH key that Phabricator will use to push to those repos, but with 300 repos you probably don’t want to do that.

I think having a global checkout “for production” isn’t really a common practice - is that the code production is using, or is it data for production? Is production serving this repository or running it?
Do you have a test suite in place to prevent a bad commit from breaking production?


Roughly, if you’re using Jenkins, you don’t need any of the Drydock and Almanac stuff

Right. But using Harbourmaster instead of Jenkins it would be nicer. Maybe in the future :slight_smile:

is that the code production is using, or is it data for production?

the code is used by production

Is production serving this repository or running it?

only running

Do you have a test suite in place to prevent a bad commit from breaking production?

Yes. We have unittests for the big part of the code.

Ok, that sounds like classic “continuous deployment”.

In a CD system, there’s normally a machine (often Jenkins) that gets notified of updates, runs CI, and if everything is green, starts deploying (Possibly with monitoring and roll-back support if things are bad).

Because you have lots of repos, this is what I think I would have done:

  1. Build a Harbormaster build-step that runs an external HTTP call and doesn’t wait for result. Make sure one of the parameters is the repository information, and possibly the commit hash.
  2. Have Herald invoke that command for each commit/push event.
  3. Build a small service that:
    3.1 listens to that call, and updates some internal data structure with “recently changed repos”.
    3.2. Once every minute / 15 seconds, checks that table and see if there’s anything there. If there is - update a staging area with whatever is needed, and tell Jenkins to start the train from there. Wait for Jenkins to complete!
  4. Jenkins takes the stuff from the staging area, runs all tests, and if it’s good - deploy to prod.
  5. Possibly move the part from Jenkins into the custom service, because I hate configuring Jenkins.
  6. Add some monitoring and dashboards into the custom service. Spin out a devops startup.

I think you can implement the “small service” part inside Jenkins, but I also think you’ll go insane trying to. Also take a look at things like BuildBot, which is an “automation framework” and not a “CI engine”.

If you had only one large repo, you could skip the “small service”, because all it’s trying to do is aggregate changes from multiple places into a single build event.

yes, at the moment it’s a classic deployment. We try to move to a modern one, but it takes time.

Thanks for your thoughts. We try to implement almost exactly how you described. Without the “small service”.
We already have jenkins implemented in our system which is running unittests and builds.

The only think that I don’t like jenkins is that you can’t run a job multiple times in the same time, like Harbourmaster builds.
In this case we have to call the jenkins job without waiting to finish in order to start building again in case there is a new commit.

I hope the Phabricator team will improve Harbourmaster :slight_smile:

PS: we are using Phabricator in production since 2 years and it got improved a LOT.