Skip to content

Releases: airvzxf/ftp-deployment-action

v2.2.0

Choose a tag to compare

@airvzxf airvzxf released this 08 Jul 01:16
v2.2.0
64a2a49

Four PRs on top of v2.1.0. No breaking change.

Added

  • PR #52 (feat(observability)) — script hardening + log
    ergonomics:

    • Shebang is now #!/bin/sh -eu and the script enables
      pipefail immediately after. Catches silent typos in
      variable names (set -u) and a failing command in a
      pipeline (pipefail).
    • ::add-mask:: for INPUT_PASSWORD, INPUT_USER and
      INPUT_SERVER so even future log lines that echo the
      value will be redacted in the rendered log.
    • ::group:: / ::endgroup:: around the four heavy
      log phases (Inputs received, Resolved configuration,
      Upload, final result) so the GitHub Actions UI can
      collapse them.
  • PR #53 (chore(devx)) — developer-experience polish:

    • .github/dependabot.yml with weekly scans for both
      docker and github-actions ecosystems. lftp bumps are
      restricted to patches; actions are grouped per major.
    • ASCII flow diagram of init.sh added to the README.
    • Makefile with lint, test, build, run, release
      and clean targets (all no-op if the underlying tool
      is not installed).
  • PR #59 (feat:) — error classification and log
    capture:

    • Permanent lftp errors (530 login, 550 permission, 550
      no such file) are detected in the captured output and
      abort the retry loop early, saving minutes of waiting
      on a backoff that would never succeed.
    • Every lftp invocation's combined stdout+stderr is
      written to a timestamped log file at
      ~/.lftp-logs/run-<UTC>.log. The path is exposed as
      the log_file action output (via $GITHUB_OUTPUT)
      so a follow-up step can attach it as a workflow
      artifact, and it is printed in the failure banner.
  • PR #60 (feat:) — dry_run input (default false).
    When set to true, the mirror command gets lftp's
    --dry-run flag, the plan is reported without any
    transfer or delete, and the final success banner switches
    to FTP DRY RUN COMPLETED (no files transferred) so a
    casual reader cannot mistake a dry run for a real upload.
    Long-standing TODO in the README.

Changed

  • Relicense to AGPL-3.0 (commit f9bfc80). The
    project moves from GPL-3.0 to the GNU Affero General
    Public License v3.0 to add the network use clause
    (section 13 of the AGPL), ensuring the source remains
    available to users interacting with the software over a
    network. No code change; only LICENSE is updated.

v2.1.0

Choose a tag to compare

@airvzxf airvzxf released this 08 Jul 01:15
v2.1.0
4b32472

Bundles three things: the release pipeline (already on main),
the digest pinning of the base image (also already on main
under [Unreleased]), and two follow-up PRs:

  • PR #50 (fix(b02,b18)) — quick wins + docs.
  • PR #51 (feat:) — deprecation warning for EOL / @latest /
    @master / older v1.x refs.

Added

  • Deprecation warning (PR #51). At the very start of init.sh,
    the action inspects $GITHUB_ACTION_REF and emits a
    ::warning::, ::notice::, or ::error:: workflow command:

    Ref Severity Reason
    @latest warning moving target, pin to @v2 or @<sha>
    @master / @main warning development branch, use a tag
    v1.0-alpha.1, v1.0-alpha.2 warning EOL (see SECURITY.md: only v1.4+ supported)
    v1.1, v1.2.0 warning EOL
    v1.3.0 … v1.3.3 warning EOL
    v1.4 … v1.9 notice v2 is available (BREAKING: ssl_verify_certificate default flipped to true)
    v2.x, anything not in the list (silent) current line

    The actual image version is baked into /app/VERSION at build
    time (ARG VERSION=<tag> from release.yml, default dev for
    local builds) and shown in the warning text.

    Strategy: hardcoded EOL list. No network call, no latency, no
    rate limit. Updated on each major-line cut (next cut: v3, when
    v2 becomes EOL).

  • fail_on_deprecated input (PR #51, default false). When
    true and the ref is EOL, the warning is upgraded to
    ::error:: and the action exits 1. Other warnings stay
    advisory. Useful for orgs with a "no EOL versions" policy.

  • Troubleshooting section in the README (PR #50). Five most
    common lftp errors (530, 550, TLS verify failed, connection
    refused, mirror access failed) with cause + fix in tabular form.

  • Header badges in the README (PR #50): CI status, latest
    release, license.

  • .github/workflows/release.yml (already on main under
    [Unreleased]): on every pushed v*.*.* tag (and via manual
    workflow_dispatch with a tag input), the workflow:

    • builds the Docker image with docker/build-push-action@v6
      using GitHub Actions cache (type=gha),
    • pushes the result to ghcr.io/airvzxf/ftp-deployment-action
      with tags <version> and v<version> plus OCI image labels
      (source, license, version),
    • generates a CycloneDX SBOM with anchore/sbom-action@v0 and
      attaches it to the image as an in-toto attestation via
      actions/attest@v4,
    • signs the image with cosign sign --yes (keyless, OIDC).
  • CI and release workflows use actions/checkout@v5 (was v4).
    v5 targets Node 24, which silences the deprecation warning
    every push produced since Node 20 was deprecated on the GH
    Actions runners. No user-facing behaviour change.

Fixed

  • B-02 (PR #50): max_retries=0 now means "retry forever"
    (the only exits are lftp success or the 5h global timeout). The
    README and action.yml description already implied this but
    init.sh treated 0 as a single attempt. The loop guard now
    skips the counter check when INPUT_MAX_RETRIES is 0.
    Non-breaking for every caller that uses the default
    max_retries=10; only callers that explicitly passed
    max_retries=0 see the change.

  • B-13 (already on main under [Unreleased]): Dockerfile
    now pins the base image by digest
    (alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659,
    the digest of alpine:3.23.3 at the time of v2.0.1). The
    package versions are pinned too (lftp=4.9.2-r9,
    ca-certificates=20260611-r0), which resolves hadolint
    DL3018. Bumping the base image is now a controlled cadence
    done via the release pipeline; the next bump will need a digest
    refresh in the same commit that bumps the tag.

  • B-18 (PR #50): README examples now use @v2 instead of
    @latest, with a callout recommending major-version pins over
    floating tags. The @latest / @main antipattern is now
    additionally enforced from inside the action via the
    deprecation warning above.

v2.0.1

Choose a tag to compare

@airvzxf airvzxf released this 05 Jul 06:59
v2.0.1
2920f30

What's new

Adds the release pipeline that turns every pushed v*.*.* tag into
a signed image at ghcr.io/airvzxf/ftp-deployment-action with an
attached CycloneDX SBOM, automatically. The pipeline:

  1. builds the Docker image with docker/build-push-action@v6,
  2. pushes to ghcr.io/airvzxf/ftp-deployment-action:v<version> and
    :<version>, with OCI image labels,
  3. generates a CycloneDX SBOM with anchore/sbom-action@v0,
  4. signs the image with cosign sign --yes (keyless, OIDC),
  5. attaches the SBOM as an in-toto attestation with actions/attest@v4.

Also pins the Dockerfile to a known-good digest
(alpine@sha256:25109184...) and pins the apk package versions,
which resolves the hadolint DL3018 warning that was open since
the first PR.

Behaviour

v2.0.1 is behaviour-preserving for end users. The action's
runs.image stays on 'Dockerfile' in action.yml (which uses
the local Dockerfile in the repo); the published
ghcr.io/airvzxf/ftp-deployment-action:v2.0.1 is a side benefit
for anyone who wants to inspect the supply chain or pull the
image outside of the action context.

The action's input contract is unchanged from v2.0.0. The next
v2.x.y tag will go through the same pipeline automatically.

Verified

  • ghcr.io/airvzxf/ftp-deployment-action:v2.0.1 is published.
  • cosign verify --certificate-identity-regexp 'https://github.com/airvzxf/ftp-deployment-action' --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' ghcr.io/airvzxf/ftp-deployment-action:v2.0.1 succeeds.
  • The SBOM is downloadable from the workflow run as
    sbom.cyclonedx.json (CycloneDX 1.6, 289 components).
  • The build workflow run is
    https://github.com/airvzxf/ftp-deployment-action/actions/runs/28730866556.

Assets

v2.0.0

Choose a tag to compare

@airvzxf airvzxf released this 05 Jul 05:19
v2.0.0
07f0ce7

⚠️ Breaking change

The default of ssl_verify_certificate is flipped from "false" to
"true". The action now refuses to connect to a server with an
invalid, expired, or hostname-mismatched certificate. The previous
default made Man-in-the-Middle attacks trivial; the new default
makes certificate validation safe-by-default.

Migration

If you connect to a server with a self-signed certificate or a
direct IP address, you must opt back into the v1.x behaviour
explicitly:

- uses: airvzxf/ftp-deployment-action@v2
  with:
    server: ${{ secrets.FTP_SERVER }}
    user:   ${{ secrets.FTP_USERNAME }}
    password: ${{ secrets.FTP_PASSWORD }}
    ssl_verify_certificate: 'false'   # opt back into v1.x behaviour

If you want to stay on the v1.x line forever and never see this
default flip, pin to the v1 major-version ref:

- uses: airvzxf/ftp-deployment-action@v1   # currently v1.5.0

Note on direct-IP connections: lftp cannot validate a
hostname against an IP-address certificate, so
ssl_verify_certificate: true requires both a valid certificate
and a hostname (not a bare IP) in the server input.

What ships in v2.0.0

Beyond the breaking default flip, v2.0.0 includes two prior
releases worth of backward-compatible work that landed on master
before this tag:

  • v1.4.0 — P0 quality fixes (PR #40): mirror_verbose and
    lftp_settings are now real inputs, integer inputs are validated
    with clear error messages, retry backoff is exponential with
    jitter, a hard 5-hour timeout wraps the lftp call, input values
    are no longer echoed to the log by default, exit codes are
    documented. CI workflow with shellcheck / actionlint / hadolint
    / contract test / smoke test on every push and PR.

  • v1.5.0 — Security hardening (PR #42): the container now
    runs as the unprivileged lftp user, the password is written
    to ~/.netrc with mode 0600 and removed via an EXIT trap
    (no longer in argv), local_dir / remote_dir are validated
    against path-traversal and shell-metacharacter injection, and
    lftp_settings is lightly sanitised (rejects !, backtick,
    dollar, control characters, more than three ;-chained
    directives).

Both v1.4.0 and v1.5.0 are behaviour-preserving — no input default
changes, no new required inputs, no new required configuration.
The only v2.0.0 behaviour change is the SSL default.

Assets

v1.3.3

Choose a tag to compare

@airvzxf airvzxf released this 29 Jan 03:40
18b3230

Improve LFTP options. Before the SSL allow was false, it meant that the password of the FTP and files was unencrypted. It changed to true, but the SSL verification certification was turned to false to avoid the error and warnings during the connection and operations. Some notes were added to the README file for this security warning.

Full Changelog: v1.3.2...v1.3.3

v1.3.2

Choose a tag to compare

@airvzxf airvzxf released this 28 Jan 18:34
361850b

What's Changed

  • [Snyk] Security upgrade alpine from 3.18.5 to 3.23.2 by @airvzxf in #37
  • [Snyk] Security upgrade alpine from 3.23.2 to 3.23.3 by @airvzxf in #38

Full Changelog: v1.3.1...v1.3.2

v1.3.0

Choose a tag to compare

@airvzxf airvzxf released this 30 Nov 08:45
43fdbd5

Focused on

  • Security scans and fixes.
  • Updated and fixed the documentation (README.md).
  • Upgraded parameters:
    • Some default values changed.
    • Displayed logs when the mirror is executing.
    • Added the lftp_settings to add any settings for the lftp command.

What's Changed

  • [Snyk] Security upgrade alpine from latest to 3.15 by @snyk-bot in #4
  • [Snyk] Security upgrade alpine from latest to 3.15.4 by @snyk-bot in #5
  • [Snyk] Security upgrade alpine from 3.15.4 to 3.15.8 by @snyk-bot in #7
  • [Snyk] Security upgrade alpine from 3.15.8 to 3.16.7 by @airvzxf in #9

Full Changelog: v1.2.0...v1.3.0

v1.2.0

Choose a tag to compare

@airvzxf airvzxf released this 30 Nov 08:42

When the connection returns an error, it will wait one minute before the next try.

Full Changelog: v1.1...v1.2.0

v1.1

Choose a tag to compare

@airvzxf airvzxf released this 25 Sep 23:05

This release version contain the follow improvements:

  • Fixed the infinite loop if an error occurred, describing the solution in the next bullet.
  • Added more lftp settings for retries and timeouts to customize your deployment.
  • If the lftp has an error connection for any reason, it will try to execute a couple more times.
  • Added the no-symlinks option.

TODOs completed:

  • Add the property/option for the upload the Symlinks.

Alpha 2

Alpha 2 Pre-release
Pre-release

Choose a tag to compare

@airvzxf airvzxf released this 13 Sep 08:07

Added more information in the Read Me file also modified the title and description for this action.