feat: initial coa-pack scaffolding (manifest + build_pack + workflow + docs)
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
* -text
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
name: release-pack
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '20*' # date-based: 2026.05.25 etc.
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
pack:
|
||||||
|
runs-on: linux-amd64
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Build pack
|
||||||
|
run: bash tools/build_pack.sh --tag "${{ github.ref_name }}"
|
||||||
|
|
||||||
|
- name: Publish release (Gitea API direct; no action dependency)
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
TAG: ${{ github.ref_name }}
|
||||||
|
API: ${{ github.server_url }}/api/v1
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
# Create the release (or reuse if it already exists for this tag).
|
||||||
|
RID=$(curl -s -H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
"$API/repos/$REPO/releases/tags/$TAG" 2>/dev/null \
|
||||||
|
| jq -r '.id // empty')
|
||||||
|
if [ -z "$RID" ]; then
|
||||||
|
RID=$(curl -sf -X POST -H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
"$API/repos/$REPO/releases" \
|
||||||
|
-d "$(jq -nc --arg t "$TAG" '{tag_name:$t,name:$t,draft:false,prerelease:false}')" \
|
||||||
|
| jq -r '.id')
|
||||||
|
fi
|
||||||
|
echo "release id: $RID"
|
||||||
|
# Upload every dist/*.zip
|
||||||
|
for f in dist/*.zip; do
|
||||||
|
name=$(basename "$f")
|
||||||
|
echo "uploading $name"
|
||||||
|
curl -sf -X POST -H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
-F "attachment=@$f" \
|
||||||
|
"$API/repos/$REPO/releases/$RID/assets?name=$name" \
|
||||||
|
| jq -r '" -> " + .browser_download_url'
|
||||||
|
done
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
.env
|
||||||
|
.DS_Store
|
||||||
|
.release
|
||||||
|
.install
|
||||||
|
.lua/*
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
dist/
|
||||||
|
tmp/
|
||||||
|
staging/
|
||||||
@@ -1,3 +1,92 @@
|
|||||||
# coa-pack
|
# coa-pack
|
||||||
|
|
||||||
Bundles every Exiles/coa-* addon's latest release into one ExilesPack-<YYYY.MM.DD>.zip for guildies.
|
Bundles every `Exiles/coa-*` addon's latest Gitea release into one
|
||||||
|
`ExilesPack-<YYYY.MM.DD>.zip` for guildies. This repo is not itself an addon;
|
||||||
|
it's the pack builder.
|
||||||
|
|
||||||
|
The output zip unpacks straight into `Interface/AddOns/` - every entry at the
|
||||||
|
top level of the zip is an addon folder.
|
||||||
|
|
||||||
|
## Cut a release
|
||||||
|
|
||||||
|
```
|
||||||
|
git tag 2026.05.25
|
||||||
|
git push --tags
|
||||||
|
```
|
||||||
|
|
||||||
|
The `.gitea/workflows/release.yml` workflow picks up any tag starting with
|
||||||
|
`20*`, runs `tools/build_pack.sh --tag <tag>`, and attaches the resulting
|
||||||
|
`ExilesPack-<tag>.zip` to a Gitea release.
|
||||||
|
|
||||||
|
The pack lands at
|
||||||
|
[git.sub-net.at/Exiles/coa-pack/releases](https://git.sub-net.at/Exiles/coa-pack/releases).
|
||||||
|
|
||||||
|
## Build locally
|
||||||
|
|
||||||
|
```
|
||||||
|
bash tools/build_pack.sh # ExilesPack-<UTC date>.zip
|
||||||
|
bash tools/build_pack.sh --tag 2026.05.25
|
||||||
|
```
|
||||||
|
|
||||||
|
Produces `dist/ExilesPack-*.zip` and prints a summary (addon count, size,
|
||||||
|
sha256, repos included/skipped).
|
||||||
|
|
||||||
|
Dependencies: `bash`, `curl`, `jq`, `unzip`, `zip`. No `yq`.
|
||||||
|
|
||||||
|
## Peek at what's currently included
|
||||||
|
|
||||||
|
```
|
||||||
|
bash tools/list_releases.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Lists every `include: true` repo in `manifest.yaml` with its latest release
|
||||||
|
tag (or `(no releases)`). Pure read-only against the public Gitea API.
|
||||||
|
|
||||||
|
## manifest.yaml
|
||||||
|
|
||||||
|
Controls which repos go in the pack. Format:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
addons:
|
||||||
|
- repo: coa-decursive
|
||||||
|
include: true
|
||||||
|
- repo: coa-ace3
|
||||||
|
include: false
|
||||||
|
note: "Ace3 libs are already embedded inside each consuming fork."
|
||||||
|
```
|
||||||
|
|
||||||
|
- `include: true` - fetch the latest release and unpack its zip assets into
|
||||||
|
the pack.
|
||||||
|
- `include: false` - leave it out (libraries, repo-only utilities,
|
||||||
|
not-yet-ready forks, etc.).
|
||||||
|
|
||||||
|
To add a new addon: append a `- repo: coa-<name>` block with `include: true`.
|
||||||
|
To drop one without deleting the entry, flip its flag to `false` (and add a
|
||||||
|
`note:` so the reason survives).
|
||||||
|
|
||||||
|
## Asset selection rules
|
||||||
|
|
||||||
|
For each included repo, the builder grabs the latest release and chooses
|
||||||
|
which zip(s) to extract:
|
||||||
|
|
||||||
|
1. If a `<repo>-all.zip` asset is present (umbrella zip with every addon
|
||||||
|
folder side-by-side), use just that.
|
||||||
|
2. Otherwise, download every `*.zip` asset attached to the release.
|
||||||
|
|
||||||
|
Each chosen zip is unpacked straight into `staging/`, where it should
|
||||||
|
produce one or more `<AddonFolder>/` entries. The per-repo zips don't carry
|
||||||
|
repo metadata (`.git`, `README.md`, `.gitea/`), so the pack stays clean.
|
||||||
|
|
||||||
|
## Failure modes
|
||||||
|
|
||||||
|
- Repo has no releases yet: warned and skipped (the pack still builds).
|
||||||
|
- Asset download fails: one retry, then warned and skipped.
|
||||||
|
- All repos skip: build errors out (`staging is empty`).
|
||||||
|
|
||||||
|
This means cutting a pack release while some addons are still tagless will
|
||||||
|
produce a smaller-than-final pack rather than a hard failure. Re-tag once
|
||||||
|
all repos have shipped.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[0BSD](LICENSE). Matches the rest of the Exiles addon repos.
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
pack:
|
||||||
|
name: ExilesPack
|
||||||
|
default_branch: master
|
||||||
|
source: https://git.sub-net.at/Exiles
|
||||||
|
|
||||||
|
# Repos to include in the pack. `include: false` means the repo is in the org but
|
||||||
|
# either contains libraries only (coa-ace3) or isn't an end-user addon.
|
||||||
|
addons:
|
||||||
|
- repo: coa-ace3 # canonical Ace3 bundle - libs, not deployed as addon
|
||||||
|
include: false
|
||||||
|
note: "Ace3 libs are already embedded inside each consuming fork; the pack doesn't need them again."
|
||||||
|
|
||||||
|
- repo: coa-ai-voiceover
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-altoholic
|
||||||
|
include: true
|
||||||
|
# If a repo has a *-all.zip release asset, prefer it over individual zips. Otherwise take all per-addon zips.
|
||||||
|
|
||||||
|
- repo: coa-atlasloot
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-bagnon
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-bartender
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-chatter
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-clique
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-dbm
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-decursive
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-details
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-elvui
|
||||||
|
include: true
|
||||||
|
note: "Repo-only - not part of the default Exiles install. Set include:false if you want a slimmer pack."
|
||||||
|
|
||||||
|
- repo: coa-exporter
|
||||||
|
include: true
|
||||||
|
note: "Guild-only addon (CoaExporter)."
|
||||||
|
|
||||||
|
- repo: coa-kui-nameplates
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-leatrix-plus
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-moveanything
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-omen
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-pawn
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-professionmenu
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-quartz
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-ratingbuster
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-sexymap
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-shadowedunitframes
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-tsm
|
||||||
|
include: true
|
||||||
|
|
||||||
|
- repo: coa-vanillaguide
|
||||||
|
include: true
|
||||||
|
note: "Repo-only - not part of the default Exiles install."
|
||||||
|
|
||||||
|
- repo: coa-weakauras
|
||||||
|
include: true
|
||||||
Executable
+204
@@ -0,0 +1,204 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# build_pack.sh - assemble ExilesPack-<YYYY.MM.DD>.zip (or ExilesPack-<tag>.zip with --tag)
|
||||||
|
#
|
||||||
|
# Reads manifest.yaml, fetches the latest release of every `include: true` repo
|
||||||
|
# from git.sub-net.at/Exiles, downloads the chosen zip asset(s), unpacks them
|
||||||
|
# into staging/, and rolls the combined result up into dist/ExilesPack-*.zip.
|
||||||
|
#
|
||||||
|
# We unpack the per-addon release zips (which contain just the addon folder,
|
||||||
|
# nothing else), so .git/.github/.gitea/README.md never enter the pack.
|
||||||
|
#
|
||||||
|
# Deps: bash, curl, jq, unzip, zip. No yq needed - the manifest is parsed
|
||||||
|
# with a tiny awk pass tailored to its simple shape.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Paths + args
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
MANIFEST="$REPO_ROOT/manifest.yaml"
|
||||||
|
TMP_DIR="$REPO_ROOT/tmp"
|
||||||
|
STAGING_DIR="$REPO_ROOT/staging"
|
||||||
|
DIST_DIR="$REPO_ROOT/dist"
|
||||||
|
|
||||||
|
API="https://git.sub-net.at/api/v1"
|
||||||
|
ORG="Exiles"
|
||||||
|
|
||||||
|
TAG=""
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--tag) TAG="$2"; shift 2 ;;
|
||||||
|
--tag=*) TAG="${1#--tag=}"; shift ;;
|
||||||
|
-h|--help)
|
||||||
|
cat <<USAGE
|
||||||
|
Usage: $0 [--tag <tag>]
|
||||||
|
|
||||||
|
Without --tag, the pack is named ExilesPack-<UTC YYYY.MM.DD>.zip.
|
||||||
|
With --tag, the pack is named ExilesPack-<tag>.zip.
|
||||||
|
USAGE
|
||||||
|
exit 0 ;;
|
||||||
|
*) echo "unknown arg: $1" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$TAG" ]; then
|
||||||
|
PACK_NAME="ExilesPack-$(date -u +%Y.%m.%d).zip"
|
||||||
|
else
|
||||||
|
PACK_NAME="ExilesPack-${TAG}.zip"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
log() { printf '[build_pack] %s\n' "$*"; }
|
||||||
|
warn() { printf '[build_pack] WARN: %s\n' "$*" >&2; }
|
||||||
|
die() { printf '[build_pack] ERROR: %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# Parse manifest.yaml -> emit "<repo>" for every `include: true` entry.
|
||||||
|
# The manifest is a small fixed-shape YAML (no anchors, no nested lists), so a
|
||||||
|
# 20-line awk pass is more honest than pulling in a yq dependency.
|
||||||
|
manifest_includes() {
|
||||||
|
awk '
|
||||||
|
/^[[:space:]]*-[[:space:]]*repo:[[:space:]]*/ {
|
||||||
|
sub(/^[[:space:]]*-[[:space:]]*repo:[[:space:]]*/, "")
|
||||||
|
gsub(/[[:space:]]+$/, "")
|
||||||
|
repo = $0
|
||||||
|
include = ""
|
||||||
|
next
|
||||||
|
}
|
||||||
|
/^[[:space:]]+include:[[:space:]]*/ {
|
||||||
|
sub(/^[[:space:]]+include:[[:space:]]*/, "")
|
||||||
|
gsub(/[[:space:]]+$/, "")
|
||||||
|
include = $0
|
||||||
|
if (repo != "" && include == "true") { print repo }
|
||||||
|
repo = ""; include = ""
|
||||||
|
}
|
||||||
|
' "$MANIFEST"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Latest release JSON for a repo, or empty string if none.
|
||||||
|
latest_release_json() {
|
||||||
|
local repo="$1" json
|
||||||
|
json="$(curl -fsS "$API/repos/$ORG/$repo/releases?limit=1" 2>/dev/null || echo '[]')"
|
||||||
|
# `releases?limit=1` returns an array. Strip the wrapper.
|
||||||
|
if [ "$(printf '%s' "$json" | jq -r 'type')" = "array" ]; then
|
||||||
|
printf '%s' "$json" | jq -c '.[0] // empty'
|
||||||
|
else
|
||||||
|
printf ''
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Download one URL with one retry. Stream to file.
|
||||||
|
fetch_with_retry() {
|
||||||
|
local url="$1" out="$2"
|
||||||
|
for attempt in 1 2; do
|
||||||
|
if curl -fsSL --retry 0 -o "$out" "$url"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
warn "download attempt $attempt failed for $url"
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Reset workspace
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
rm -rf "$TMP_DIR" "$STAGING_DIR"
|
||||||
|
mkdir -p "$TMP_DIR" "$STAGING_DIR" "$DIST_DIR"
|
||||||
|
|
||||||
|
INCLUDED=0
|
||||||
|
SKIPPED=0
|
||||||
|
SKIPPED_REPOS=()
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Per-repo: pick assets, download, extract
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
while read -r repo; do
|
||||||
|
[ -z "$repo" ] && continue
|
||||||
|
log "processing $repo"
|
||||||
|
|
||||||
|
rel="$(latest_release_json "$repo")"
|
||||||
|
if [ -z "$rel" ] || [ "$rel" = "null" ]; then
|
||||||
|
warn "$repo has no releases yet - skipping"
|
||||||
|
SKIPPED=$((SKIPPED + 1))
|
||||||
|
SKIPPED_REPOS+=("$repo")
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
tag="$(printf '%s' "$rel" | jq -r '.tag_name')"
|
||||||
|
log " latest release: $tag"
|
||||||
|
|
||||||
|
# Prefer <repo>-all.zip if present, otherwise take every *.zip asset.
|
||||||
|
assets_all="$(printf '%s' "$rel" | jq -r --arg n "${repo}-all.zip" \
|
||||||
|
'.assets[]? | select(.name == $n) | .browser_download_url')"
|
||||||
|
if [ -n "$assets_all" ]; then
|
||||||
|
download_urls="$assets_all"
|
||||||
|
log " using ${repo}-all.zip"
|
||||||
|
else
|
||||||
|
download_urls="$(printf '%s' "$rel" | jq -r \
|
||||||
|
'.assets[]? | select(.name | endswith(".zip")) | .browser_download_url')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$download_urls" ]; then
|
||||||
|
warn "$repo release $tag has no .zip assets - skipping"
|
||||||
|
SKIPPED=$((SKIPPED + 1))
|
||||||
|
SKIPPED_REPOS+=("$repo")
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$TMP_DIR/$repo"
|
||||||
|
any_ok=0
|
||||||
|
while IFS= read -r url; do
|
||||||
|
[ -z "$url" ] && continue
|
||||||
|
name="$(basename "$url")"
|
||||||
|
log " downloading $name"
|
||||||
|
if fetch_with_retry "$url" "$TMP_DIR/$repo/$name"; then
|
||||||
|
if unzip -q -o "$TMP_DIR/$repo/$name" -d "$STAGING_DIR"; then
|
||||||
|
any_ok=1
|
||||||
|
else
|
||||||
|
warn " unzip failed for $name"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn " could not download $name after retry"
|
||||||
|
fi
|
||||||
|
done <<<"$download_urls"
|
||||||
|
|
||||||
|
if [ "$any_ok" -eq 1 ]; then
|
||||||
|
INCLUDED=$((INCLUDED + 1))
|
||||||
|
else
|
||||||
|
SKIPPED=$((SKIPPED + 1))
|
||||||
|
SKIPPED_REPOS+=("$repo")
|
||||||
|
fi
|
||||||
|
done < <(manifest_includes)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Build the pack
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if [ ! -d "$STAGING_DIR" ] || [ -z "$(ls -A "$STAGING_DIR" 2>/dev/null)" ]; then
|
||||||
|
die "staging is empty - no addons were successfully fetched"
|
||||||
|
fi
|
||||||
|
|
||||||
|
OUT="$DIST_DIR/$PACK_NAME"
|
||||||
|
rm -f "$OUT"
|
||||||
|
( cd "$STAGING_DIR" && zip -qr "$OUT" . )
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Summary
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
ADDON_FOLDERS="$(cd "$STAGING_DIR" && find . -mindepth 1 -maxdepth 1 -type d | wc -l)"
|
||||||
|
SIZE="$(stat -c '%s' "$OUT")"
|
||||||
|
SHA="$(sha256sum "$OUT" | awk '{print $1}')"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "============================================================"
|
||||||
|
echo " pack: $PACK_NAME"
|
||||||
|
echo " path: $OUT"
|
||||||
|
echo " addon dirs: $ADDON_FOLDERS"
|
||||||
|
echo " size: $SIZE bytes"
|
||||||
|
echo " sha256: $SHA"
|
||||||
|
echo " repos in: $INCLUDED"
|
||||||
|
echo " repos out: $SKIPPED${SKIPPED_REPOS[*]:+ (${SKIPPED_REPOS[*]})}"
|
||||||
|
echo "============================================================"
|
||||||
Executable
+42
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# List the most-recent release tag for every includable repo in manifest.yaml.
|
||||||
|
# Pure read-only via Gitea API. No auth needed - the Exiles org repos are public.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
MANIFEST="$REPO_ROOT/manifest.yaml"
|
||||||
|
|
||||||
|
API="https://git.sub-net.at/api/v1"
|
||||||
|
ORG="Exiles"
|
||||||
|
|
||||||
|
# Same minimal-awk parser as build_pack.sh.
|
||||||
|
manifest_includes() {
|
||||||
|
awk '
|
||||||
|
/^[[:space:]]*-[[:space:]]*repo:[[:space:]]*/ {
|
||||||
|
sub(/^[[:space:]]*-[[:space:]]*repo:[[:space:]]*/, "")
|
||||||
|
gsub(/[[:space:]]+$/, "")
|
||||||
|
repo = $0; include = ""; next
|
||||||
|
}
|
||||||
|
/^[[:space:]]+include:[[:space:]]*/ {
|
||||||
|
sub(/^[[:space:]]+include:[[:space:]]*/, "")
|
||||||
|
gsub(/[[:space:]]+$/, "")
|
||||||
|
include = $0
|
||||||
|
if (repo != "" && include == "true") { print repo }
|
||||||
|
repo = ""; include = ""
|
||||||
|
}
|
||||||
|
' "$MANIFEST"
|
||||||
|
}
|
||||||
|
|
||||||
|
while read -r repo; do
|
||||||
|
[ -z "$repo" ] && continue
|
||||||
|
json="$(curl -fsS "$API/repos/$ORG/$repo/releases?limit=1" 2>/dev/null || echo '[]')"
|
||||||
|
tag="$(printf '%s' "$json" | jq -r '.[0].tag_name // empty')"
|
||||||
|
date="$(printf '%s' "$json" | jq -r '.[0].published_at // empty' | cut -dT -f1)"
|
||||||
|
if [ -z "$tag" ]; then
|
||||||
|
printf '%-30s (no releases)\n' "$repo"
|
||||||
|
else
|
||||||
|
printf '%-30s %-20s %s\n' "$repo" "$tag" "$date"
|
||||||
|
fi
|
||||||
|
done < <(manifest_includes)
|
||||||
Reference in New Issue
Block a user