Skip to content

Commit 032b746

Browse files
authored
move all actions to brewkit (#38)
* move all actions to `brewkit` * move scripts * prep for merge * make actions/scripts top-level; move utils
1 parent 059f0d2 commit 032b746

File tree

18 files changed

+1085
-0
lines changed

18 files changed

+1085
-0
lines changed

.github/workflows/retagger.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
on:
2+
release:
3+
types:
4+
- created
5+
- edited
6+
permissions:
7+
contents: write
8+
jobs:
9+
retag:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v3
13+
- uses: fischerscode/tagger@v0
14+
with:
15+
prefix: v

actions/bottle/action.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: tea/pantry/bottle
2+
description: internal tea.xyz specific at this time
3+
4+
inputs:
5+
gpg-key-id:
6+
description: gpg key id
7+
required: true
8+
gpg-key-passphrase:
9+
description: gpg key passphrase
10+
required: true
11+
built:
12+
description: packages to bottle
13+
required: true
14+
compression:
15+
description: compression to use (gz or xz)
16+
required: true
17+
outputs:
18+
bottles:
19+
description: bottle files
20+
value: ${{ steps.bottle.outputs.bottles }}
21+
checksums:
22+
description: checksum files
23+
value: ${{ steps.bottle.outputs.checksums }}
24+
signatures:
25+
description: signature files
26+
value: ${{ steps.bottle.outputs.signatures }}
27+
28+
runs:
29+
using: composite
30+
steps:
31+
- run: ${{ github.action_path }}/bottle.ts ${{ inputs.built }}
32+
id: bottle
33+
shell: sh
34+
env:
35+
COMPRESSION: ${{ inputs.compression }}
36+
GPG_KEY_ID: ${{ inputs.gpg-key-id }}
37+
GPG_PASSPHRASE: ${{ inputs.gpg-passphrase }}

actions/bottle/bottle.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/env tea
2+
3+
/* ---
4+
dependencies:
5+
gnu.org/tar: ^1.34
6+
tukaani.org/xz: ^5
7+
zlib.net: 1
8+
gnupg.org: ^2
9+
args:
10+
- deno
11+
- run
12+
- --allow-net
13+
- --allow-run
14+
- --allow-env
15+
- --allow-read
16+
- --allow-write
17+
--- */
18+
19+
import { Installation } from "types"
20+
import { useCellar, usePrefix, useFlags, useCache } from "hooks"
21+
import { backticks, panic, run } from "utils"
22+
import { crypto } from "deno/crypto/mod.ts"
23+
import { encode } from "deno/encoding/hex.ts"
24+
import { encode as base64Encode } from "deno/encoding/base64.ts"
25+
import { set_output } from "../../libexec/utils/gha.ts"
26+
import * as ARGV from "../../libexec/utils/args.ts"
27+
import Path from "path"
28+
29+
const cellar = useCellar()
30+
31+
32+
//-------------------------------------------------------------------------- main
33+
34+
if (import.meta.main) {
35+
useFlags()
36+
37+
const compression = Deno.env.get("COMPRESSION") == 'xz' ? 'xz' : 'gz'
38+
const gpgKey = Deno.env.get("GPG_KEY_ID") ?? panic("missing GPG_KEY_ID")
39+
const gpgPassphrase = Deno.env.get("GPG_PASSPHRASE") ?? panic("missing GPG_PASSPHRASE")
40+
const checksums: string[] = []
41+
const signatures: string[] = []
42+
const bottles: Path[] = []
43+
44+
for await (const pkg of ARGV.pkgs()) {
45+
console.log({ bottling: pkg })
46+
47+
const installation = await cellar.resolve(pkg)
48+
const path = await bottle(installation, compression)
49+
const checksum = await sha256(path)
50+
const signature = await gpg(path, { gpgKey, gpgPassphrase })
51+
52+
console.log({ bottled: path })
53+
54+
bottles.push(path)
55+
checksums.push(checksum)
56+
signatures.push(signature)
57+
}
58+
59+
await set_output("bottles", bottles.map(b => b.relative({ to: usePrefix() })))
60+
await set_output("checksums", checksums)
61+
await set_output("signatures", signatures)
62+
}
63+
64+
65+
//------------------------------------------------------------------------- funcs
66+
export async function bottle({ path: kegdir, pkg }: Installation, compression: 'gz' | 'xz'): Promise<Path> {
67+
const tarball = useCache().path({ pkg, type: 'bottle', compression })
68+
const z = compression == 'gz' ? 'z' : 'J'
69+
const cwd = usePrefix()
70+
const cmd = ["tar", `c${z}f`, tarball, kegdir.relative({ to: cwd })]
71+
await run({ cmd, cwd })
72+
return tarball
73+
}
74+
75+
export async function sha256(file: Path): Promise<string> {
76+
return await Deno.open(file.string, { read: true })
77+
.then(file => crypto.subtle.digest("SHA-256", file.readable))
78+
.then(buf => new TextDecoder().decode(encode(new Uint8Array(buf))))
79+
}
80+
81+
interface GPGCredentials {
82+
gpgKey: string
83+
gpgPassphrase: string
84+
}
85+
86+
async function gpg(file: Path, { gpgKey, gpgPassphrase }: GPGCredentials): Promise<string> {
87+
const rv = await backticks({
88+
cmd: [
89+
"gpg",
90+
"--detach-sign",
91+
"--armor",
92+
"--output",
93+
"-",
94+
"--local-user",
95+
gpgKey,
96+
"--passphrase",
97+
gpgPassphrase,
98+
file.string
99+
]
100+
})
101+
return base64Encode(rv)
102+
}

actions/cache/action.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: tea.xyz/pantry/actions/cache
2+
description: cache deno deps
3+
4+
inputs:
5+
cache-name:
6+
description: name of the job to use on the cache key
7+
required: true
8+
9+
runs:
10+
using: composite
11+
steps:
12+
- run: |
13+
if test "$RUNNER_OS" = "macOS"; then
14+
echo "cache=~/Library/Caches/deno" >> $GITHUB_OUTPUT
15+
else
16+
echo "cache=~/.cache/deno" >> $GITHUB_OUTPUT
17+
fi
18+
id: os-cache
19+
shell: sh
20+
21+
- uses: actions/cache@v3
22+
with:
23+
path: |
24+
~/.deno
25+
${{ steps.os-cache.outputs.cache }}
26+
# This isn't perfect (can't hash stuff outside github.workspace, and if the there scripts change, the hash won't)
27+
# but it's good enough for now. It's slightly conservative, since it monitors all .ts files, but that's fine.
28+
key: ${{ runner.os }}-deno-${{ inputs.cache-name }}-${{ hashFiles('**/deno.jsonc', '**/*.ts') }}

actions/codesign/action.yml

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
name: Apple Codesigning
2+
description: Codesigns macOS binaries
3+
4+
inputs:
5+
p12-file-base64:
6+
description: Base64 encoded p12 file
7+
required: true
8+
p12-password:
9+
description: Password for p12 file
10+
required: true
11+
identity:
12+
description: Identity to use for signing
13+
required: true
14+
paths:
15+
description: paths to sign
16+
required: true
17+
18+
runs:
19+
using: composite
20+
steps:
21+
# Only runs on macOS
22+
- name: Check platform
23+
shell: sh
24+
run: |
25+
if [[ "$RUNNER_OS" != "macOS" ]]; then
26+
echo "This action only runs on macOS"
27+
exit 1
28+
fi
29+
30+
# the next three steps bless our code for Apple. It might be the case they should be
31+
# encapulated separately.
32+
# FIXME: using an explicit commit in a PR isn't great, but the last release was almost 3 years
33+
# ago, and we need bugfixes.
34+
# FIXME: replace this with a tea script based on https://localazy.com/blog/how-to-automatically-sign-macos-apps-using-github-actions
35+
# github has a doc with similar content, but it's not returning to me atm.
36+
37+
# apple-actions/import-codesign-certs will fail if the keychain already exists, so we prophylactically
38+
# delete it if it does.
39+
- name: Delete keychain
40+
shell: sh
41+
run: security delete-keychain signing_temp.keychain || true
42+
43+
- uses: apple-actions/import-codesign-certs@d54750db52a4d3eaed0fc107a8bab3958f3f7494
44+
with:
45+
p12-file-base64: ${{ inputs.p12-file-base64 }}
46+
p12-password: ${{ inputs.p12-password }}
47+
48+
- name: Create file list
49+
shell: sh
50+
id: files
51+
run: |
52+
echo "sign<<EOF" >> $GITHUB_OUTPUT
53+
/usr/bin/find $PATHS \
54+
-type f \
55+
-not -name '*.py' \
56+
-not -name '*.pyc' \
57+
-not -name '*.txt' \
58+
-not -name '*.h' | \
59+
/usr/bin/sed -e 's/ /\\ /g' >> $GITHUB_OUTPUT
60+
echo "EOF" >> $GITHUB_OUTPUT
61+
62+
# `tea` won't pass strict checking due to a deno bug with the way
63+
# MachO headers are created
64+
# https://github.com/denoland/deno/issues/17753
65+
echo "check<<EOF" >> $GITHUB_OUTPUT
66+
/usr/bin/find $PATHS \
67+
-type f \
68+
-not -name '*.py' \
69+
-not -name '*.pyc' \
70+
-not -name '*.txt' \
71+
-not -name '*.h' \
72+
-not -name tea | \
73+
/usr/bin/sed -e 's/ /\\ /g' >> $GITHUB_OUTPUT
74+
echo "EOF" >> $GITHUB_OUTPUT
75+
env:
76+
PATHS: ${{ inputs.paths }}
77+
78+
- name: Codesign files
79+
shell: sh
80+
run: |
81+
echo "$FILES" | \
82+
/usr/bin/xargs /usr/bin/codesign -s "$IDENTITY" --force -v --timestamp || true
83+
env:
84+
FILES: ${{ steps.files.outputs.sign }}
85+
IDENTITY: ${{ inputs.identity }}
86+
87+
# This isn't very informative, but even a no-op is safer than none
88+
- name: Check codesigning
89+
shell: sh
90+
run: echo "$FILES" | /usr/bin/xargs /usr/bin/codesign -vvv --strict
91+
env:
92+
FILES: ${{ steps.files.outputs.check }}
93+
94+
# Needed for self-hosted runner, since it doesn't destroy itself automatically.
95+
- name: Delete keychain
96+
if: always()
97+
shell: sh
98+
run: security delete-keychain signing_temp.keychain
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: tea/pantry/fetch-pr-artifacts
2+
description: internal tea.xyz specific at this time
3+
4+
inputs:
5+
platform:
6+
description: platform+arch to fetch
7+
required: true
8+
token:
9+
description: github token
10+
default: ${{ github.token }}
11+
required: true
12+
AWS_S3_BUCKET:
13+
description: AWS S3 bucket to use for cache
14+
required: true
15+
AWS_ACCESS_KEY_ID:
16+
description: AWS access key id
17+
required: true
18+
AWS_SECRET_ACCESS_KEY:
19+
description: AWS secret access key
20+
required: true
21+
22+
runs:
23+
using: composite
24+
steps:
25+
- run:
26+
${{ github.action_path }}/fetch-pr-artifacts.ts
27+
${{ github.repository }}
28+
${{ github.sha }}
29+
${{ inputs.platform }} >>$GITHUB_ENV
30+
shell: sh
31+
env:
32+
GITHUB_TOKEN: ${{ inputs.token }}
33+
AWS_S3_BUCKET: ${{ inputs.AWS_S3_BUCKET }}
34+
AWS_ACCESS_KEY_ID: ${{ inputs.AWS_ACCESS_KEY_ID }}
35+
AWS_SECRET_ACCESS_KEY: ${{ inputs.AWS_SECRET_ACCESS_KEY }}

0 commit comments

Comments
 (0)