Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,33 @@ jobs:
run: |
version=$(ruby -rjson -e "print JSON.parse(File.read('../.release-please-manifest.json'))['.']")
printf 'tag_name=v%s\n' "$version" >> "$GITHUB_OUTPUT"
-
# Covers the app + Hex deps + the actual Erlang/OTP and Elixir
# versions this release was built with (mix_sbom includes those by
# default - verified they show up as individual OTP application
# components like kernel/stdlib/crypto/ssl, not one umbrella
# "erlang" entry). Not the container image's own OS packages -
# that's a separate SBOM, generated in the container job below,
# since it's meaningless to anyone not using the container.
name: Generate the app SBOM
uses: erlef/mix_sbom@v0
id: sbom
with:
project-path: ${{ github.workspace }}/app
reuse-beam: true
schema: "1.6"
format: "json"
-
# mix_sbom has no output-filename input - it always writes to
# $RUNNER_TEMP/$RANDOM.cdx.json internally (verified directly in
# the action's own script) and exposes that random path via
# sbom-path. gh release create uses a file's own basename as the
# asset's actual download filename (the `#label` syntax only sets
# a cosmetic display label, not the filename) - without this
# rename, the Readme's documented .../download/sbom.cdx.json URL
# would 404.
name: Rename the SBOM to a stable filename
run: mv "${{ steps.sbom.outputs.sbom-path }}" sbom.cdx.json
-
# One atomic command creates the tag, the release, and uploads
# every asset together - no separate release object sits around
Expand All @@ -163,7 +190,7 @@ jobs:
name: Create the release with every asset attached
env:
GH_TOKEN: ${{ github.token }}
run: gh release create "${{ steps.version.outputs.tag_name }}" burrito_out/*.tar.gz burrito_out/SHA256SUMS --generate-notes
run: gh release create "${{ steps.version.outputs.tag_name }}" burrito_out/*.tar.gz burrito_out/SHA256SUMS sbom.cdx.json --generate-notes
working-directory: app
-
# release-please labels its own release PR "autorelease: pending"
Expand Down Expand Up @@ -215,9 +242,34 @@ jobs:
env:
APP_VERSION: ${{ needs.burrito.outputs.tag_name }}
run: ./ci/build_image.sh "${{ needs.burrito.outputs.tag_name }}"
-
# Covers the container's own OS packages (Alpine/apk - ca-certificates,
# bash) - meaningless to anyone not using the container, which is
# exactly why it's a separate file from the app SBOM in the burrito
# job above, not merged into it. Scans the local image ci/build_image.sh
# just built (tagged <repo-basename>:<tag_name> - no registry prefix),
# before publishing, though nothing here gates the publish step on it.
name: Generate the container SBOM
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: "linear-cli-ex:${{ needs.burrito.outputs.tag_name }}"
scan-type: image
format: cyclonedx
output: container-sbom.cdx.json
-
name: Publish the image
env:
GITHUB_TOKEN: ${{ github.token }}
GITHUB_ACTOR: ${{ github.actor }}
run: ./ci/publish.sh "${{ needs.burrito.outputs.tag_name }}"
-
# Can't attach this to the GitHub release the burrito job already
# created - it's published (and thus immutable, see #18) by the time
# this job runs. A workflow artifact is the honest option here, not
# a release asset pretending to be one.
name: Upload the container SBOM as a workflow artifact
uses: actions/upload-artifact@v7
with:
name: container-sbom
path: container-sbom.cdx.json
retention-days: 90
6 changes: 6 additions & 0 deletions Readme.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ $ sudo mv lc/* /usr/local/bin/
----
<1> Linux: `sha256sum -c -` instead of `shasum -a 256 -c -`.

Every release also publishes `sbom.cdx.json` - a CycloneDX SBOM covering the
app, its Hex dependencies, and the Erlang/OTP and Elixir versions it was
built with. The container image has its own separate SBOM (its OS packages -
irrelevant if you're not using the container), published as a workflow
artifact on the release's build rather than a release asset.

NOTE: A Homebrew tap is planned but not yet available.

On macOS, Gatekeeper blocks `lc` itself (the wrapper scripts are plain shell,
Expand Down
3 changes: 2 additions & 1 deletion app/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ defmodule LinearCli.MixProject do
{:burrito, "~> 1.6"},
{:sourceror, "~> 1.8", only: [:dev, :test]},
{:ash, "~> 3.0"},
{:igniter, "~> 0.6", only: [:dev, :test]}
{:igniter, "~> 0.6", only: [:dev, :test]},
{:sbom, "~> 0.10", only: :dev, runtime: false}
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
]
Expand Down
4 changes: 4 additions & 0 deletions app/mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"exqlite": {:hex, :exqlite, "0.39.0", "08f8ff646e14d14bab289f4cfc61b95ffc5fe7c82d420049a480eee69ca8b292", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "603de0f7637adc88275fa12ccbd58954ff6000f75386e876565b49032d9aede9"},
"finch": {:hex, :finch, "0.23.0", "e3f9287ac25a8832f848b144c2b57346aac65b205e2e0629a52adfe6507fd837", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.8", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "80e58d3f936f57e3fdf404f83a3642897ae6d9fb642934e46da4d8fe761b99d5"},
"glob_ex": {:hex, :glob_ex, "0.1.12", "7b2d9369c20e2697efcfd185d13d6e84c94cd3bfd2730fbde613141c2e015c00", [:mix], [], "hexpm", "2e2fac83f113514434c7eaf267b4c38af2f91766f1cab2c5db7053b7fc1ee0bb"},
"hex_core": {:hex, :hex_core, "0.15.0", "8eadc0ccb08e3742f2313073d04f39eaa7904617329039e9d3c402f5dd227673", [:rebar3], [], "hexpm", "c2093764c7af8ef0818c104fa141eba431e7be93f8374638c45c7037b26a52f8"},
"hpax": {:hex, :hpax, "1.0.4", "777de5d433b0fbdc7c418159c8055910faa8047ffdb3d6b31098d2a46cd7685c", [:mix], [], "hexpm", "afc7cb142ebcc2d01ce7816190b98ce5dd49e799111b24249f3443d730f377ca"},
"igniter": {:hex, :igniter, "0.8.3", "9de74d3885efae43b0b58dc6f7b816963c4bbd391e6b6fe6922ee21c4e384c76", [:mix], [{:ex_ast, "~> 0.5", [hex: :ex_ast, repo: "hexpm", optional: false]}, {:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4.5", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "afc5e3848d885e680da5c3b65e5e7717555a08cd12305190ff2be76427af39ff"},
"iterex": {:hex, :iterex, "0.1.2", "58f9b9b9a22a55cbfc7b5234a9c9c63eaac26d276b3db80936c0e1c60355a5a6", [:mix], [], "hexpm", "2e103b8bcc81757a9af121f6dc0df312c9a17220f302b1193ef720460d03029d"},
Expand All @@ -33,10 +34,13 @@
"plug": {:hex, :plug, "1.20.3", "56c480c633ec2ce10140e236e15233bf576e1d323887d7c96711bd02ab5160db", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "be266aee1b8536ef6409d58cf39a3121319f0ec47cfa1b24024485aa0e76ad76"},
"plug_crypto": {:hex, :plug_crypto, "2.2.0", "144014737daaf485407f5ed77daeaad74d651b216a28c87543f8cc7043f8efc8", [:mix], [], "hexpm", "83a95744ab1c75876542b6fab135fcc176280e0f301a111c1f757fddcec95d2c"},
"postgrex": {:hex, :postgrex, "0.22.4", "d271f595dfd25230b6398354e19d17bb5e2d20130fd2d9bdca7e15f125d43552", [:mix], [{:db_connection, "~> 2.9", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "4aae45a2d60e35b04eea2602440be152fae332901f1fc7a60fc7cb7f0f9a9c5a"},
"protobuf": {:hex, :protobuf, "0.16.1", "7bf0c3e6b24fd297e2e3722ecb985c3900bcc55d004b7faf0737e248c3e64691", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "97fb9f1fe48c38c4ebfc0f8a7a20adb22b2491bbfbdb192fab9a6821d5d604e1"},
"purl": {:hex, :purl, "0.3.0", "b6e2f792e944007ed474299e56ea43ea4819f064a4a4f67e4153e1ba02d066de", [:mix, :rebar3], [{:stream_data, "~> 1.1", [hex: :stream_data, repo: "hexpm", optional: true]}], "hexpm", "158cf2c0533517fe0d68752bd790392f065c5ffdfbde38524890066ebe640302"},
"reactor": {:hex, :reactor, "1.0.4", "e19b3bc00776935282e5e0fc1b0dcb87682ec6f3c238ce3150fe7f50bbde502a", [:mix], [{:igniter, "~> 0.4", [hex: :igniter, repo: "hexpm", optional: true]}, {:iterex, "~> 0.1", [hex: :iterex, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:multigraph, "~> 0.16.1-mg.2", [hex: :multigraph, repo: "hexpm", optional: false]}, {:spark, ">= 2.3.3 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}, {:ymlr, "~> 5.0", [hex: :ymlr, repo: "hexpm", optional: false]}], "hexpm", "ba5ec7c9ddc7781f65b6e9818456c4c6b4e997a9a926d83b6f3f12192ef5b395"},
"req": {:hex, :req, "0.7.2", "364eae2e5f5c984f2dac6d71c07f8c8c89ce0bc49c4d746dacb7a306823020de", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:finch, "~> 0.21", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "c9cdfa276b05d8db2a27fda5d233e6858b764d47189d76cbb186e130a871ae0b"},
"rewrite": {:hex, :rewrite, "1.3.0", "67448ba7975690b35ba7e7f35717efcce317dbd5963cb0577aa7325c1923121a", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "d111ac7ff3a58a802ef4f193bbd1831e00a9c57b33276e5068e8390a212714a5"},
"rustler_precompiled": {:hex, :rustler_precompiled, "0.9.0", "3a052eda09f3d2436364645cc1f13279cf95db310eb0c17b0d8f25484b233aa0", [:mix], [{:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "471d97315bd3bf7b64623418b3693eedd8e47de3d1cb79a0ac8f9da7d770d94c"},
"sbom": {:hex, :sbom, "0.10.0", "b99be5407bc196d0ad71b8061126a67aae46dc3bfaa852b4c1c04645dd1ad984", [:mix], [{:hex_core, "~> 0.15.0", [hex: :hex_core, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}, {:optimus, "~> 0.6.1", [hex: :optimus, repo: "hexpm", optional: false]}, {:protobuf, "~> 0.16.0", [hex: :protobuf, repo: "hexpm", optional: false]}, {:purl, "~> 0.3.0", [hex: :purl, repo: "hexpm", optional: false]}], "hexpm", "a8116ef965c1ebd103e223545794bd0a6691edd3ec678ec07972d473e2badc95"},
"sourceror": {:hex, :sourceror, "1.12.2", "85bfd48159f020c0cbfc72f289f11456fdc05dc43719b6f2589fb969faefa113", [:mix], [], "hexpm", "da37d3da09c5b890528802c7056a8f585a061973820d7656b6e3649c14f0e9cb"},
"spark": {:hex, :spark, "2.7.2", "36becc6ff03b40908cc821d403d7f06d893498e293d2f718afc6ca097fcb9d93", [:mix], [{:igniter, ">= 0.3.64 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: true]}], "hexpm", "adb323ddbf9dbbe326f9e5def54ac96c47911e852b2c270bb19a5147c56f1b45"},
"spitfire": {:hex, :spitfire, "0.4.0", "6d98c10cf585434b9439ba0c6dd3cc7aeff0e06ab73bfe5488f42e7c0f883d9b", [:mix], [], "hexpm", "7e5c6d1523c111b59f332f9dc49edc0377111d0c17167a29830f0e98233f5472"},
Expand Down
Loading