Skip to content

Commit d99aa02

Browse files
authored
Merge pull request #160 from learningequality/automate-ppa-releases
feat: Automate PPA releases with Launchpad API workflows
2 parents bb80094 + 3ad9949 commit d99aa02

11 files changed

Lines changed: 1496 additions & 435 deletions

.github/workflows/prerelease.yml

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
name: Pre-release to kolibri-proposed PPA
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
tar-url:
7+
description: 'URL for Kolibri tar file'
8+
required: true
9+
workflow_call:
10+
inputs:
11+
tar-file-name:
12+
required: false
13+
type: string
14+
tar-url:
15+
required: false
16+
type: string
17+
ref:
18+
description: 'A ref for this workflow to check out its own repo'
19+
required: false
20+
type: string
21+
outputs:
22+
version:
23+
description: "Full Debian version string (e.g. 0.19.3-0ubuntu1)"
24+
value: ${{ jobs.build_source_and_upload.outputs.version }}
25+
secrets:
26+
GPG_SIGNING_KEY:
27+
required: true
28+
GPG_PASSPHRASE:
29+
required: true
30+
LP_CREDENTIALS:
31+
required: true
32+
33+
jobs:
34+
build_source_and_upload:
35+
name: Build, sign, and upload source package
36+
runs-on: ubuntu-latest
37+
outputs:
38+
version: ${{ steps.version.outputs.version }}
39+
steps:
40+
- uses: actions/checkout@v6
41+
if: ${{ !inputs.ref }}
42+
- uses: actions/checkout@v6
43+
if: ${{ inputs.ref }}
44+
with:
45+
repository: learningequality/kolibri-installer-debian
46+
ref: ${{ inputs.ref }}
47+
- uses: actions/cache@v5
48+
with:
49+
path: ~/.cache/pip
50+
key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }}
51+
restore-keys: |
52+
${{ runner.os }}-pip-
53+
- name: Download tarfile from URL
54+
if: ${{ inputs.tar-url }}
55+
env:
56+
TAR_URL: ${{ inputs.tar-url }}
57+
run: make get-tar tar="$TAR_URL"
58+
- name: Download tarfile from artifacts
59+
if: ${{ inputs.tar-file-name }}
60+
uses: actions/download-artifact@v8
61+
with:
62+
name: ${{ inputs.tar-file-name }}
63+
path: build_src
64+
- name: Install build and upload dependencies
65+
run: |
66+
sudo apt-get update
67+
sudo apt-get install -y devscripts debhelper python3-pip dput \
68+
dh-python python3-all python3-pytest python3-launchpadlib \
69+
python3-distro-info
70+
- name: Install Python dependencies
71+
run: pip install .
72+
- name: Extract version
73+
id: version
74+
run: |
75+
make dist/VERSION
76+
VERSION=$(cat dist/VERSION)
77+
# Convert to Debian version format (match version_to_debian in generate_changelog.py)
78+
DEB_VERSION=$(echo "$VERSION" | sed 's/-\(alpha\|beta\|rc\)/~\1/g' | sed 's/\.dev/~dev/g')
79+
FULL_VERSION="${DEB_VERSION}-0ubuntu1"
80+
echo "version=$FULL_VERSION" >> "$GITHUB_OUTPUT"
81+
echo "Package version: $FULL_VERSION"
82+
- name: Write Launchpad credentials
83+
env:
84+
LP_CREDENTIALS: ${{ secrets.LP_CREDENTIALS }}
85+
run: echo "$LP_CREDENTIALS" > /tmp/lp-creds.txt
86+
- name: Check if source already uploaded
87+
id: check_source
88+
env:
89+
LP_CREDENTIALS_FILE: /tmp/lp-creds.txt
90+
run: |
91+
set +e
92+
python3 scripts/launchpad_copy.py check-source \
93+
--package kolibri-source \
94+
--version "${{ steps.version.outputs.version }}"
95+
rc=$?
96+
set -e
97+
if [ "$rc" -eq 0 ]; then
98+
echo "already_uploaded=true" >> "$GITHUB_OUTPUT"
99+
elif [ "$rc" -eq 1 ]; then
100+
echo "already_uploaded=false" >> "$GITHUB_OUTPUT"
101+
else
102+
echo "::error::Failed to check source package status"
103+
exit "$rc"
104+
fi
105+
- name: Configure GPG Key
106+
if: steps.check_source.outputs.already_uploaded != 'true'
107+
run: |
108+
echo -n "${{ secrets.GPG_SIGNING_KEY }}" | base64 --decode | gpg --import --no-tty --batch --yes
109+
echo "allow-loopback-pinentry" >> ~/.gnupg/gpg-agent.conf
110+
gpgconf --kill gpg-agent
111+
- name: Build and upload source package
112+
if: steps.check_source.outputs.already_uploaded != 'true'
113+
env:
114+
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
115+
run: make commit-new-release
116+
- name: Cleanup credentials
117+
if: always()
118+
run: rm -f /tmp/lp-creds.txt
119+
120+
wait_for_source_published:
121+
name: Wait for source package builds
122+
needs: build_source_and_upload
123+
runs-on: ubuntu-latest
124+
steps:
125+
- uses: actions/checkout@v6
126+
if: ${{ !inputs.ref }}
127+
- uses: actions/checkout@v6
128+
if: ${{ inputs.ref }}
129+
with:
130+
repository: learningequality/kolibri-installer-debian
131+
ref: ${{ inputs.ref }}
132+
- name: Install dependencies
133+
run: |
134+
sudo apt-get update
135+
sudo apt-get install -y python3-launchpadlib python3-distro-info
136+
- name: Write Launchpad credentials
137+
env:
138+
LP_CREDENTIALS: ${{ secrets.LP_CREDENTIALS }}
139+
run: echo "$LP_CREDENTIALS" > /tmp/lp-creds.txt
140+
- name: Wait for published binaries in source series
141+
env:
142+
LP_CREDENTIALS_FILE: /tmp/lp-creds.txt
143+
run: |
144+
SERIES=$(lsb_release -cs)
145+
python3 scripts/launchpad_copy.py wait-for-published \
146+
--version "${{ needs.build_source_and_upload.outputs.version }}" \
147+
--series "$SERIES"
148+
- name: Cleanup Launchpad credentials
149+
if: always()
150+
run: rm -f /tmp/lp-creds.txt
151+
152+
copy_to_other_series:
153+
name: Copy to all supported Ubuntu series
154+
needs:
155+
- build_source_and_upload
156+
- wait_for_source_published
157+
runs-on: ubuntu-latest
158+
steps:
159+
- uses: actions/checkout@v6
160+
if: ${{ !inputs.ref }}
161+
- uses: actions/checkout@v6
162+
if: ${{ inputs.ref }}
163+
with:
164+
repository: learningequality/kolibri-installer-debian
165+
ref: ${{ inputs.ref }}
166+
- name: Install dependencies
167+
run: |
168+
sudo apt-get update
169+
sudo apt-get install -y python3-launchpadlib python3-distro-info
170+
- name: Write Launchpad credentials
171+
env:
172+
LP_CREDENTIALS: ${{ secrets.LP_CREDENTIALS }}
173+
run: echo "$LP_CREDENTIALS" > /tmp/lp-creds.txt
174+
- name: Copy to other supported series
175+
env:
176+
LP_CREDENTIALS_FILE: /tmp/lp-creds.txt
177+
run: |
178+
python3 scripts/launchpad_copy.py copy-to-series
179+
- name: Cleanup Launchpad credentials
180+
if: always()
181+
run: rm -f /tmp/lp-creds.txt
182+
183+
wait_for_copies_published:
184+
name: Wait for all series builds
185+
needs:
186+
- build_source_and_upload
187+
- copy_to_other_series
188+
runs-on: ubuntu-latest
189+
steps:
190+
- uses: actions/checkout@v6
191+
if: ${{ !inputs.ref }}
192+
- uses: actions/checkout@v6
193+
if: ${{ inputs.ref }}
194+
with:
195+
repository: learningequality/kolibri-installer-debian
196+
ref: ${{ inputs.ref }}
197+
- name: Install dependencies
198+
run: |
199+
sudo apt-get update
200+
sudo apt-get install -y python3-launchpadlib python3-distro-info
201+
- name: Write Launchpad credentials
202+
env:
203+
LP_CREDENTIALS: ${{ secrets.LP_CREDENTIALS }}
204+
run: echo "$LP_CREDENTIALS" > /tmp/lp-creds.txt
205+
- name: Wait for all builds to complete
206+
env:
207+
LP_CREDENTIALS_FILE: /tmp/lp-creds.txt
208+
run: |
209+
python3 scripts/launchpad_copy.py wait-for-published \
210+
--version "${{ needs.build_source_and_upload.outputs.version }}"
211+
- name: Cleanup Launchpad credentials
212+
if: always()
213+
run: rm -f /tmp/lp-creds.txt
Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
1-
name: Publish Debian Repository
1+
name: Release PPA
22

33
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: 'Debian package version to promote (e.g. 0.19.3-0ubuntu1)'
8+
required: true
9+
deb-url:
10+
description: 'URL to download a .deb file (for GitHub Pages PPA)'
11+
required: false
412
workflow_call:
513
inputs:
14+
version:
15+
description: 'Debian package version to promote'
16+
required: true
17+
type: string
618
deb-file-name:
7-
description: 'Artifact name of the .deb file (from build_deb workflow)'
19+
description: 'Artifact name of the .deb file (from build workflow)'
820
required: true
921
type: string
1022
debian-repo-signing-key-id:
11-
description: 'Debian repo signing key ID (falls back to vars.DEBIAN_REPO_SIGNING_KEY_ID if not set)'
23+
description: 'Debian repo signing key ID (falls back to vars.DEBIAN_REPO_SIGNING_KEY_ID)'
1224
required: false
1325
type: string
1426
secrets:
27+
LP_CREDENTIALS:
28+
required: true
1529
DEBIAN_REPO_SIGNING_KEY:
1630
required: true
1731
LE_BOT_APP_ID:
@@ -20,26 +34,54 @@ on:
2034
LE_BOT_PRIVATE_KEY:
2135
description: 'GitHub App Private Key for cross-repo authentication'
2236
required: true
23-
workflow_dispatch:
24-
inputs:
25-
deb-url:
26-
description: 'URL to download a .deb file'
27-
required: true
28-
type: string
29-
30-
concurrency:
31-
group: debian-repo-publish
32-
cancel-in-progress: false
33-
34-
permissions:
35-
contents: read
3637

3738
jobs:
38-
publish:
39+
promote:
40+
name: Promote packages from kolibri-proposed to kolibri
41+
runs-on: ubuntu-latest
42+
steps:
43+
- name: Checkout codebase
44+
uses: actions/checkout@v6
45+
with:
46+
repository: learningequality/kolibri-installer-debian
47+
ref: ${{ github.ref }}
48+
- name: Install dependencies
49+
run: |
50+
sudo apt-get update
51+
sudo apt-get install -y python3-launchpadlib python3-distro-info
52+
- name: Write Launchpad credentials
53+
env:
54+
LP_CREDENTIALS: ${{ secrets.LP_CREDENTIALS }}
55+
run: echo "$LP_CREDENTIALS" > /tmp/lp-creds.txt
56+
- name: Promote from kolibri-proposed to kolibri
57+
env:
58+
LP_CREDENTIALS_FILE: /tmp/lp-creds.txt
59+
run: |
60+
python3 scripts/launchpad_copy.py promote \
61+
--version "${{ inputs.version }}"
62+
- name: Wait for promoted packages to be published
63+
env:
64+
LP_CREDENTIALS_FILE: /tmp/lp-creds.txt
65+
run: |
66+
python3 scripts/launchpad_copy.py wait-for-published \
67+
--version "${{ inputs.version }}" \
68+
--ppa kolibri
69+
- name: Cleanup Launchpad credentials
70+
if: always()
71+
run: rm -f /tmp/lp-creds.txt
72+
73+
publish_github_pages_ppa:
74+
name: Publish GitHub Pages PPA
3975
runs-on: ubuntu-latest
76+
concurrency:
77+
group: debian-repo-publish
78+
cancel-in-progress: false
4079
steps:
4180
- name: Checkout repository
4281
uses: actions/checkout@v6
82+
with:
83+
repository: learningequality/kolibri-installer-debian
84+
ref: ${{ github.ref }}
4385

4486
- name: Install dependencies
4587
run: sudo apt-get update && sudo apt-get install -y reprepro
@@ -56,7 +98,8 @@ jobs:
5698
5799
- name: Import GPG signing key into isolated keyring
58100
run: |
59-
export GNUPGHOME=$(mktemp -d)
101+
GNUPGHOME=$(mktemp -d)
102+
export GNUPGHOME
60103
echo "GNUPGHOME=$GNUPGHOME" >> "$GITHUB_ENV"
61104
echo "pinentry-mode loopback" > "$GNUPGHOME/gpg.conf"
62105
echo "allow-loopback-pinentry" > "$GNUPGHOME/gpg-agent.conf"
@@ -73,7 +116,11 @@ jobs:
73116

74117
- name: Download .deb from URL
75118
if: inputs.deb-url
76-
run: make get-deb deb="${{ inputs.deb-url }}"
119+
env:
120+
DEB_URL: ${{ inputs.deb-url }}
121+
run: |
122+
mkdir -p incoming
123+
wget -P incoming/ "$DEB_URL"
77124
78125
- name: Identify .deb file
79126
id: deb

0 commit comments

Comments
 (0)