Skip to content

Publish local file to log

Attaches a file produced by the job to the job log, so anyone reading the log can download it from the browser. Use it for build output, test reports, generated configuration, the things people ask for after a job finishes.

The copy lives with the log and dies with it. When the log is purged the attachment goes too. For a file that has to survive, publish it into a repository with Publish files to the artifacts repository instead, or send it to a node with Ship File Remotely.

The op needs a running job. In a rule type that does not run inside a job there is no log to publish into and the op fails.

File Path out/report.html 1. under the job directory 2. exactly as written one log entry Store Data? on Store Data? off contents stored, download link name only, no download neither path exists: the op fails and names both paths it tried

Fields

File Path

The file to publish, on the machine running the job. ${var} placeholders expand against the stash.

The value is tried twice. First it is joined onto the job directory, so out/report.html finds the file the job produced. If nothing is there, it is used exactly as written, so /tmp/foo/report.html also works. That fallback is why an absolute path needs no prefix, and why ${job_dir}/out/report.html and out/report.html land on the same file.

When neither attempt finds a file, the op fails with a message naming both paths it tried, after variable expansion. That message is the fastest way to see what your placeholders resolved to.

One file per op, and no wildcards. Loop with FOREACH file/item to publish a set.

Leave the field blank and nothing complains. The empty value resolves to the job directory itself, which exists, so the search above succeeds on the first attempt and you get a log entry with a blank name and nothing behind it. Check this field first when a job logs a nameless attachment.

Filename

The name the file is given in the log. Leave it blank and the base name of the file found on disk is used, which is the right answer most of the time.

Set it when the file on disk has a working name you do not want people to see, a temporary name with a hash in it for example, or when several ops publish files whose base names collide.

The download does not arrive under this name alone. The browser is offered the job identifier and the log line number in front of it, as <job mid>-<log id>-<your name>, so the saved file always carries that prefix.

${var} placeholders expand. The value is a name, not a path. A slash in it creates no directory, it becomes part of the name.

Store Data?

Ticked, the file contents are read and stored with the log entry, and the log row shows a download link. The size appears beside the link once the file passes 4 KB, and not at all below that.

A zero-byte file is the exception. The line still appears, but the row carries no download link at all, ticked or not.

Unticked, only the name is recorded. The row still shows an icon for the file type, but there is nothing behind it to click. That is the setting for large files, or for when you want the log to record that an artifact was produced without carrying a copy of it.

The box is ticked on a fresh op. An op whose form has never had this field touched behaves as ticked too.

The whole file is read into memory before it is stored, and the stored copy is compressed. The op imposes no size limit of its own, so a multi-gigabyte artifact will stall the job and bloat the log. Untick the box for anything that is not a report or a small package.

Text contents are scanned for password-shaped strings and those are masked before storage. Binary contents are stored untouched. The log line gets the same masking whether the box is ticked or not.

Message

Extra text shown next to the file name in the log. ${var} placeholders expand.

The log line is always the file name, then the message: report.html: coverage for myapp. The file name prefix is not optional, so repeating the name inside the message reads badly.

%1 inside the message is replaced by the file name, which gives you control over where the name appears in a longer sentence. The message also goes through the translation table, so a message that matches a known phrase comes out in the reader's language.

Leave it blank and the log line is the file name on its own.

A line longer than 2000 characters, counting the file name and the separator, is cut at 2000 and marked (continue...). The tail is appended to the stored attachment, under a row of = signs, so you can only read it back when Store Data? is ticked. This is a caption field, not a place to dump command output.

What ends up in the log

One entry at info level, flagged as a milestone, which prints the line in bold and fills the Milestone column that the log's debug filter reveals. The entry is tied to the job step that was running, so it lands in the right place in the tree.

The op returns nothing, so setting a Return Key on it stores an empty value. There is no way to read back the id of the entry it created.

Nothing is undone on rollback. A rolled-back job keeps whatever it published on the way forward.

The attachment is deleted the first time the job is purged, along with the debug rows for that job. The log line survives the purge, so the job keeps saying the file was published while the download behind it returns nothing. See Purge Daemon Configuration for how long that takes. The file on disk is untouched by any of this.

Combining with other ops

Produce the file first with Run command or local script or Write local file, then publish it.

To publish several files, wrap the op in FOREACH file/item and put the loop variable in File Path.

Publish a directory as a single attachment by packing it first with Zip local path and pointing this op at the archive.

When the file matters beyond the life of the log, pair it with Publish files to the artifacts repository and publish to both.

Examples

Attach a coverage report the job produced, with a name and a caption.

File Path    reports/coverage.html
Filename     coverage-${version}.html
Store Data?  ticked
Message      coverage for ${project}

Record that a large package was built, without carrying a copy in the log.

File Path    ${job_dir}/dist/myapp-${version}.tar.gz
Filename     (blank)
Store Data?  unticked
Message      built on ${job_name}

Publish every file a previous step listed in the stash.

FOREACH file/item   over ${built_files}   as foo_file
  File Path    dist/${foo_file}
  Filename     (blank)
  Store Data?  ticked
  Message      artifact %1