Something went wrong. Try again.
CLI that turns LLM input into well-formatted Conventional Commits
Something went wrong. Try again.
12 kB · 401 lines
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402#!/usr/bin/env ysh
# SPDX-FileCopyrightText: Amolith <amolith@secluded.site>## SPDX-License-Identifier: CC0-1.0
const collection = 'sh.tangled.repo.artifact'const maximum_blob_size = 50 * 1024 * 1024const maximum_scan_limit = 10000const go = $(mise which go)const upx = $(mise which upx)const targets = [ {os: 'linux', arch: 'amd64', extension: '', pack: true, static: true}, {os: 'linux', arch: 'arm64', extension: '', pack: true, static: true}, {os: 'darwin', arch: 'amd64', extension: '', pack: false, static: false}, {os: 'darwin', arch: 'arm64', extension: '', pack: false, static: false}, {os: 'windows', arch: 'amd64', extension: '.exe', pack: false, static: false}, {os: 'freebsd', arch: 'amd64', extension: '', pack: false, static: true},]
var build_only = falsevar tag = ''if (len(ARGV) === 2 and ARGV[0] === '--build-only') { setvar build_only = true setvar tag = ARGV[1]} elif (len(ARGV) !== 0) { echo "Usage: $0 [--build-only TAG]" >&2 exit 2}
proc build_artifacts (staging, release_tag) { install -d -m 0755 "$staging" for target in (targets) { var suffix = "$[target.os]-$[target.arch]$[target.extension]" var artifact = "$staging/git-format-$release_tag-$suffix" var ldflags = "-s -w -buildid= -X main.version=$release_tag" if (target.static) { setvar ldflags = "-d $ldflags -extldflags=-static" } echo "Building $[target.os]/$[target.arch] ..." env \ CGO_ENABLED=0 \ GOOS="$[target.os]" \ GOARCH="$[target.arch]" \ GOTOOLCHAIN=go1.27.0 \ "$go" build \ -trimpath \ -tags 'netgo,osusergo,static_build' \ -ldflags "$ldflags" \ -o "$artifact" \ . if (target.pack) { echo "Packing $[target.os]/$[target.arch] ..." "$upx" --best --lzma --quiet "$artifact" "$upx" --test --quiet "$artifact" } }}
proc verify_artifacts (staging, release_tag) { for target in (targets) { var artifact = "$staging/git-format-$release_tag-$[target.os]-$[target.arch]$[target.extension]" if ! test -s "$artifact" { echo "Expected artifact is missing or empty: $artifact" >&2 exit 1 } var size = int($(wc -c < "$artifact")) if (size > maximum_blob_size) { echo "Artifact exceeds the 50 MiB lexicon limit: $artifact" >&2 exit 1 } }}
if (build_only) { if (tag !~ '^v.+$') { echo "Build-only tag must start with v: $tag" >&2 exit 2 } rm -rf -- dist build_artifacts dist "$tag" verify_artifacts dist "$tag" echo "Built $[len(targets)] release artifacts for $tag in dist/" exit 0}
const pds = get(ENV, 'GIT_FORMAT_RELEASE_PDS', 'https://sequeste.red')const identifier = get(ENV, 'GIT_FORMAT_RELEASE_IDENTIFIER', 'secluded.site')const password_file = get(ENV, 'GIT_FORMAT_RELEASE_APP_PASSWORD_FILE', '')const scan_limit_text = get(ENV, 'GIT_FORMAT_RELEASE_RECORD_SCAN_LIMIT', '100')
if (password_file === '') { echo 'GIT_FORMAT_RELEASE_APP_PASSWORD_FILE is required' >&2 exit 1}if ! test -s "$password_file" { echo "App password file is missing or empty: $password_file" >&2 exit 1}if (scan_limit_text !~ '^[0-9]+$') { echo 'GIT_FORMAT_RELEASE_RECORD_SCAN_LIMIT must be a positive integer' >&2 exit 1}const scan_limit = int(scan_limit_text)if (scan_limit < 1 or scan_limit > maximum_scan_limit) { echo "GIT_FORMAT_RELEASE_RECORD_SCAN_LIMIT must be between 1 and $maximum_scan_limit" >&2 exit 1}if (pds !~ '^https://') { echo 'GIT_FORMAT_RELEASE_PDS must use HTTPS' >&2 exit 1}if (pds ~ '/$') { echo 'GIT_FORMAT_RELEASE_PDS must not end with a slash' >&2 exit 1}
const required_environment = [ 'TANGLED_PIPELINE_KIND', 'TANGLED_REF', 'TANGLED_REF_NAME', 'TANGLED_REF_TYPE', 'TANGLED_SHA', 'TANGLED_REPO_DID', 'TANGLED_REPO_REPO_DID',]for name in (required_environment) { if (get(ENV, name, '') === '') { echo "$name is required" >&2 exit 1 }}
setvar tag = ENV.TANGLED_REF_NAMEif (ENV.TANGLED_PIPELINE_KIND !== 'push') { echo 'Releases require a push pipeline' >&2 exit 1}if (ENV.TANGLED_REF_TYPE !== 'tag') { echo 'Releases require a tag ref' >&2 exit 1}if (ENV.TANGLED_REF !== "refs/tags/$tag") { echo "TANGLED_REF does not match TANGLED_REF_NAME: $[ENV.TANGLED_REF]" >&2 exit 1}if (tag !~ '^v.+$') { echo "Release tag must start with v: $tag" >&2 exit 1}
const work = $(mktemp -d)trap --add EXIT { rm -rf -- "$work"}chmod 0700 "$work"
const tag_object = $(git rev-parse --verify "$tag^{tag}")if (tag_object !== ENV.TANGLED_SHA) { echo 'The pushed tag object does not match TANGLED_SHA' >&2 exit 1}const release_commit = $(git rev-parse --verify "$tag^{}")const checkout_commit = $(git rev-parse --verify HEAD)if (release_commit !== checkout_commit) { echo 'The annotated tag does not resolve to the checked-out commit' >&2 exit 1}const tag_bytes = $(printf '%s' "$tag_object" | xxd -r -p | base64 -w 0 | tr -d '=')if (len(tag_bytes) !== 27) { echo 'The annotated tag object ID is not a 20-byte SHA-1 value' >&2 exit 1}
const auth_config = "$work/curl-auth.conf"const password = $(cat -- "$password_file")var session_request = { identifier: identifier, password: password,}json write (session_request) > "$work/session-request.json"chmod 0600 "$work/session-request.json"
const curl_arguments = [ '--silent', '--show-error', '--fail-with-body', '--connect-timeout', '10', '--max-time', '120',]
echo 'Authenticating to the release PDS ...'curl @curl_arguments \ --request POST \ --header 'Content-Type: application/json' \ --data-binary "@$work/session-request.json" \ "$pds/xrpc/com.atproto.server.createSession" \ --output "$work/session.json"var session = nulltry { json read (&session) < "$work/session.json" 2>/dev/null}if (_status !== 0) { echo 'PDS returned an invalid session response' >&2 exit 1}const publisher_did = session.didconst access_token = session.accessJwtif (publisher_did !== ENV.TANGLED_REPO_DID) { echo 'Authenticated PDS account does not match TANGLED_REPO_DID' >&2 exit 1}printf 'header = "Authorization: Bearer %s"\n' "$access_token" > "$auth_config"chmod 0600 "$auth_config"
proc scan_release (output_path) { var matches = [] var cursor = '' var scanned = 0 while (scanned < scan_limit) { var page_size = scan_limit - scanned if (page_size > 100) { setvar page_size = 100 } var cursor_arguments = [] if (cursor !== '') { setvar cursor_arguments = ['--data-urlencode', "cursor=$cursor"] } curl @curl_arguments \ --retry 2 \ --retry-all-errors \ --get \ --data-urlencode "repo=$publisher_did" \ --data-urlencode "collection=$collection" \ --data-urlencode "limit=$page_size" \ @cursor_arguments \ "$pds/xrpc/com.atproto.repo.listRecords" \ --output "$work/records-page.json"
var page = null json read (&page) < "$work/records-page.json" for record in (page.records) { setvar scanned += 1 var value = record.value var record_tag = get(value, 'tag', {}) var record_tag_bytes = get(record_tag, '$bytes', '') if ( get(value, 'repoDid', '') === ENV.TANGLED_REPO_REPO_DID and (record_tag_bytes === tag_bytes or record_tag_bytes === "$tag_bytes=") ) { call matches->append(record) } } if (len(page.records) === 0 or scanned >= scan_limit) { break } setvar cursor = get(page, 'cursor', '') if (cursor === '') { break } } json write (matches) > "$output_path"}
echo "Checking the newest $scan_limit artifact records for $tag ..."scan_release "$work/preflight-records.json"var preflight_records = nulljson read (&preflight_records) < "$work/preflight-records.json"if (len(preflight_records) !== 0) { echo "This release already has published artifacts in the newest $scan_limit records" >&2 echo 'Create and push a new tag instead of overwriting a release' >&2 exit 1}
const staging = "$work/dist"build_artifacts "$staging" "$tag"verify_artifacts "$staging" "$tag"
var records = []const created_at = $(date --utc '+%Y-%m-%dT%H:%M:%SZ')for target in (targets) { const name = "git-format-$tag-$[target.os]-$[target.arch]$[target.extension]" const artifact = "$staging/$name" const size = int($(wc -c < "$artifact")) echo "Uploading $name ..." curl @curl_arguments \ --config "$auth_config" \ --request POST \ --header 'Content-Type: application/octet-stream' \ --data-binary "@$artifact" \ "$pds/xrpc/com.atproto.repo.uploadBlob" \ --output "$work/blob.json" var upload = null json read (&upload) < "$work/blob.json" if (upload.blob.size !== size) { echo "PDS returned invalid blob metadata for $name" >&2 exit 1 } call records->append({ '$type': collection, name: name, repoDid: ENV.TANGLED_REPO_REPO_DID, tag: {'$bytes': tag_bytes}, createdAt: created_at, artifact: upload.blob, })}
curl @curl_arguments \ --retry 2 \ --retry-all-errors \ --get \ --data-urlencode "did=$publisher_did" \ "$pds/xrpc/com.atproto.sync.getLatestCommit" \ --output "$work/latest-commit.json"var latest_commit = nulljson read (&latest_commit) < "$work/latest-commit.json"
# Read the commit before rechecking so swapCommit rejects any concurrent write# between this point and applyWrites.echo 'Rechecking publication state before creating records ...'scan_release "$work/prewrite-records.json"var prewrite_records = nulljson read (&prewrite_records) < "$work/prewrite-records.json"if (len(prewrite_records) !== 0) { echo 'Another pipeline published this release while artifacts were being prepared' >&2 echo 'Create and push a new tag instead of overwriting a release' >&2 exit 1}
var writes = []for record in (records) { call writes->append({ '$type': 'com.atproto.repo.applyWrites#create', collection: collection, value: record, })}var apply_request = { repo: publisher_did, swapCommit: latest_commit.cid, writes: writes,}json write (apply_request) > "$work/apply-request.json"
echo 'Publishing artifact records ...'var http_status = ''try { setvar http_status = $(curl @curl_arguments \ --config "$auth_config" \ --request POST \ --header 'Content-Type: application/json' \ --data-binary "@$work/apply-request.json" \ --output "$work/apply-response.json" \ --write-out '%{http_code}' \ "$pds/xrpc/com.atproto.repo.applyWrites")}const apply_curl_status = _status
# A response can be lost after the PDS commits. Reconcile before deciding# whether publication failed; never blindly repeat applyWrites.echo 'Confirming published artifact records ...'scan_release "$work/published-records.json"var published_records = nulljson read (&published_records) < "$work/published-records.json"var exact_publication = len(published_records) === len(records)for expected in (records) { var match_count = 0 for published in (published_records) { var value = published.value var value_tag = get(value, 'tag', {}) var value_tag_bytes = get(value_tag, '$bytes', '') if (value_tag_bytes === "$tag_bytes=") { setvar value.tag = {'$bytes': tag_bytes} } if (value === expected) { setvar match_count += 1 } } if (match_count !== 1) { setvar exact_publication = false }}
if (exact_publication) { echo "Published $tag with $[len(records)] artifacts" exit 0}if (len(published_records) !== 0) { echo 'Publication left an ambiguous or partial release state' >&2 echo 'Inspect the PDS records and create a new tag before retrying' >&2 exit 1}if (apply_curl_status !== 0 or http_status !~ '^2[0-9][0-9]$') { echo "PDS did not publish the release (curl status $apply_curl_status, HTTP $http_status)" >&2 echo 'No matching artifact records were found; this tag may be retried after diagnosing the failure' >&2 exit 1}
echo 'PDS accepted applyWrites but the expected records were not found' >&2exit 1