diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2103,6 +2103,7 @@
     'PhabricatorSourceCodeView' => 'view/layout/PhabricatorSourceCodeView.php',
     'PhabricatorStandardCustomField' => 'infrastructure/customfield/standard/PhabricatorStandardCustomField.php',
     'PhabricatorStandardCustomFieldBool' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php',
+    'PhabricatorStandardCustomFieldBuildableType' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldBuildableType.php',
     'PhabricatorStandardCustomFieldCredential' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php',
     'PhabricatorStandardCustomFieldDate' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php',
     'PhabricatorStandardCustomFieldHeader' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php',
@@ -4966,6 +4967,7 @@
     'PhabricatorSourceCodeView' => 'AphrontView',
     'PhabricatorStandardCustomField' => 'PhabricatorCustomField',
     'PhabricatorStandardCustomFieldBool' => 'PhabricatorStandardCustomField',
+    'PhabricatorStandardCustomFieldBuildableType' => 'PhabricatorStandardCustomField',
     'PhabricatorStandardCustomFieldCredential' => 'PhabricatorStandardCustomField',
     'PhabricatorStandardCustomFieldDate' => 'PhabricatorStandardCustomField',
     'PhabricatorStandardCustomFieldHeader' => 'PhabricatorStandardCustomField',
diff --git a/src/applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php
--- a/src/applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php
+++ b/src/applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php
@@ -12,12 +12,39 @@
   }
 
   public function getDescription() {
+
+    $values = array();
+    $publish = $this->getSetting('publish_on');
+    $publish_text = '';
+    if (in_array("commit", $publish)) {
+      $values[] = pht('commits');
+    }
+    if (in_array("revision", $publish)) {
+      $values[] = pht('differential revisions');
+    }
+    if (count($values) === 0) {
+      $publish_text = pht('Never');
+    } else {
+      $publish_text = pht('For %s,', implode(' and ', $values));
+    }
+
     return pht(
-      'Publish file artifact %s as fragment %s.',
+      '%s publish file artifact %s as fragment %s.',
+      $publish_text,
       $this->formatSettingForDescription('artifact'),
       $this->formatSettingForDescription('path'));
   }
 
+  public function logBehaviour(
+    HarbormasterBuild $build,
+    HarbormasterBuildTarget $build_target,
+    $message) {
+    $log_behaviour = $build->createLog($build_target, "publish", "result");
+    $start_behaviour = $log_behaviour->start();
+    $log_behaviour->append($message);
+    $log_behaviour->finalize($start_behaviour);
+  }
+
   public function execute(
     HarbormasterBuild $build,
     HarbormasterBuildTarget $build_target) {
@@ -25,6 +52,31 @@
     $settings = $this->getSettings();
     $variables = $build_target->getVariables();
 
+    // Check if we should publish for this buildable.
+    $buildable = $build->getBuildable();
+    $object = $buildable->getBuildableObject();
+    if ($object instanceof PhabricatorRepositoryCommit) {
+      if (!in_array("commit", $settings["publish_on"])) {
+        $this->logBehaviour(
+          $build,
+          $build_target,
+          "Not publishing because this is a commit and this step only ".
+          "publishes for ".implode(', ', $settings["publish_on"]));
+        return;
+      }
+    } elseif ($object instanceof DifferentialDiff) {
+      if (!in_array("revision", $settings["publish_on"])) {
+        $this->logBehaviour(
+          $build,
+          $build_target,
+          "Not publishing because this is a revision and this step only ".
+          "publishes for ".implode(', ', $settings["publish_on"]));
+        return;
+      }
+    } else {
+      throw new Exception("Unknown buildable type!");
+    }
+
     $path = $this->mergeVariables(
       'vsprintf',
       $settings['path'],
@@ -53,6 +105,11 @@
         $fragment->updateFromFile(PhabricatorUser::getOmnipotentUser(), $file);
       }
     }
+
+    $this->logBehaviour(
+      $build,
+      $build_target,
+      "The artifact was published successfully.");
   }
 
   public function getArtifactInputs() {
@@ -77,6 +134,10 @@
         'type' => 'text',
         'required' => true,
       ),
+      'publish_on' => array(
+        'name' => pht('Publish On'),
+        'type' => 'buildabletype',
+      ),
     );
   }
 
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBuildableType.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBuildableType.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBuildableType.php
@@ -0,0 +1,168 @@
+<?php
+
+final class PhabricatorStandardCustomFieldBuildableType
+  extends PhabricatorStandardCustomField {
+
+  public function getFieldType() {
+    return 'buildabletype';
+  }
+
+  public function buildFieldIndexes() {
+    $indexes = array();
+
+    $value = $this->getFieldValue();
+    if (strlen($value)) {
+      $indexes[] = $this->newStringIndex($value);
+    }
+
+    return $indexes;
+  }
+
+  public function readApplicationSearchValueFromRequest(
+    PhabricatorApplicationSearchEngine $engine,
+    AphrontRequest $request) {
+    return $request->getArr($this->getFieldKey());
+  }
+
+  public function applyApplicationSearchConstraintToQuery(
+    PhabricatorApplicationSearchEngine $engine,
+    PhabricatorCursorPagedPolicyAwareQuery $query,
+    $value) {
+    if ($value) {
+      $query->withApplicationSearchContainsConstraint(
+        $this->newStringIndex(null),
+        $value);
+    }
+  }
+
+  public function appendToApplicationSearchForm(
+    PhabricatorApplicationSearchEngine $engine,
+    AphrontFormView $form,
+    $value,
+    array $handles) {
+
+    if (!is_array($value)) {
+      $value = array();
+    }
+    $value = array_fuse($value);
+
+    $control = id(new AphrontFormCheckboxControl())
+      ->setLabel($this->getFieldName());
+
+    foreach ($this->getOptions() as $name => $option) {
+      $control->addCheckbox(
+        $this->getFieldKey().'[]',
+        $name,
+        $option,
+        isset($value[$name]));
+    }
+
+    $form->appendChild($control);
+  }
+
+  private function getOptions() {
+    return array(
+      'PhabricatorRepositoryCommit' => pht('Commits'),
+      'DifferentialRevision' => pht('Differential Revisions'));
+  }
+
+  public function renderEditControl(array $handles) {
+    return id(new AphrontFormCheckboxControl())
+      ->setLabel($this->getFieldName())
+      ->setCaption($this->getCaption())
+      ->setName($this->getFieldKey())
+      ->addCheckbox(
+        $this->getFieldKey().'commit',
+        'PhabricatorRepositoryCommit',
+        'Commits',
+        in_array('commit', $this->getFieldValue()))
+      ->addCheckbox(
+        $this->getFieldKey().'revision',
+        'DifferentialRevision',
+        'Differential Revisions',
+        in_array('revision', $this->getFieldValue()));
+  }
+
+  public function renderPropertyViewValue(array $handles) {
+    if (!strlen($this->getFieldValue())) {
+      return null;
+    }
+    return idx($this->getOptions(), $this->getFieldValue());
+  }
+
+  public function readValueFromRequest(AphrontRequest $request) {
+    $value = array();
+    if (strlen($request->getStr($this->getFieldKey()."commit"))) {
+      $value[] = 'commit';
+    }
+    if (strlen($request->getStr($this->getFieldKey()."revision"))) {
+      $value[] = 'revision';
+    }
+    $this->setFieldValue($value);
+  }
+
+  public function getApplicationTransactionTitle(
+    PhabricatorApplicationTransaction $xaction) {
+    $author_phid = $xaction->getAuthorPHID();
+    $old = $xaction->getOldValue();
+    $new = $xaction->getNewValue();
+
+    // TODO: This doesn't work right because the transaction system isn't
+    // storing the arrays correctly.  Both $old and $new are always null.
+
+    if ($old === null) {
+      $old = array();
+    }
+
+    if ($new === null) {
+      $new = array();
+    }
+
+    $commit_old = array_key_exists('commit', $old);
+    $commit_new = array_key_exists('commit', $new);
+    $revision_old = array_key_exists('revision', $old);
+    $revision_new = array_key_exists('revision', $new);
+
+    $commit_change = '';
+    if ($commit_old && !$commit_new) {
+      $commit_change = pht(
+        'disabled %s for commits',
+        $this->getFieldName());
+    } elseif (!$commit_old && $commit_new) {
+      $commit_change = pht(
+        'enabled %s for commits',
+        $this->getFieldName());
+    }
+
+    $revision_change = '';
+    if ($revision_old && !$revision_new) {
+      $revision_change = pht(
+        'disabled %s for revisions',
+        $this->getFieldName());
+    } elseif (!$revision_old && $revision_new) {
+      $revision_change = pht(
+        'enabled %s for revisions',
+        $this->getFieldName());
+    }
+
+    $final_change = '';
+    if (!empty($commit_change) && !empty($revision_change)) {
+      $final_change = $commit_change.', '.$revision_change;
+    } else {
+      $final_change = $commit_change.$revision_change;
+    }
+
+    if (!empty($final_change)) {
+      return pht(
+        '%s %s.',
+        $xaction->renderHandleLink($author_phid),
+        $final_change);
+    }
+
+    return pht(
+      '%s set %s to the defaults',
+      $xaction->renderHandleLink($author_phid),
+      $this->getFieldName());
+  }
+
+}