Skip to content

Update libs from CodeOnTheGo #49

Update libs from CodeOnTheGo

Update libs from CodeOnTheGo #49

Workflow file for this run

name: Update libs from CodeOnTheGo
on:
workflow_dispatch:
permissions:
contents: write
env:
PLUGINS_REMOTE_PATH: public_html/flags/plugins
jobs:
release:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Checkout plugin-examples
uses: actions/checkout@v4
with:
# PAT from a repo admin so the libs/ push bypasses the main ruleset.
# A fine-grained PAT must use the organization as its resource owner,
# not a personal account, or every push returns 403. The next step
# checks this. GITHUB_TOKEN is only a fallback to keep checkout
# working; it cannot bypass the ruleset, so the check rejects it.
token: ${{ secrets.ADMIN_PERSONAL_ACCESS_TOKEN || github.token }}
# The build below takes ~30 minutes. Without this guard, a token that
# cannot write to this repo throws all of that away at "Commit updated
# jars". Check the token first so the run fails in seconds instead.
- name: Verify the push token
env:
ADMIN_PAT: ${{ secrets.ADMIN_PERSONAL_ACCESS_TOKEN }}
GH_TOKEN: ${{ secrets.ADMIN_PERSONAL_ACCESS_TOKEN || github.token }}
run: |
org="${GITHUB_REPOSITORY%%/*}"
if [ -z "$ADMIN_PAT" ]; then
echo "::error::ADMIN_PERSONAL_ACCESS_TOKEN is not set. GITHUB_TOKEN cannot bypass the ${org} main ruleset, so the push would fail."
exit 1
fi
if ! repo=$(gh api "repos/${GITHUB_REPOSITORY}" 2>/dev/null); then
echo "::error::The token cannot see ${GITHUB_REPOSITORY}. A fine-grained PAT must use resource owner '${org}' (the organization). A token owned by a personal account cannot reach ${org} repositories."
exit 1
fi
who=$(gh api user --jq .login 2>/dev/null || echo '(unknown)')
push=$(printf '%s' "$repo" | jq -r '.permissions.push // false')
echo "Token identity: ${who}. Push permission: ${push}."
if [ "$push" != "true" ]; then
echo "::error::The token authenticates as '${who}' but cannot write to ${GITHUB_REPOSITORY}. Grant it 'Contents: Read and write' with resource owner '${org}'."
exit 1
fi
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v3
with:
cache-disabled: true
add-job-summary: 'never'
- name: Compute release tag
id: tag
env:
RUN_NUMBER: ${{ github.run_number }}
run: echo "name=build-$(date -u +%Y-%m-%d)-${RUN_NUMBER}" >> "$GITHUB_OUTPUT"
- name: Run update-libs script
run: ./scripts/update-libs.sh
- name: Commit updated jars
id: commit
run: |
sha=$(git -C .cache/CodeOnTheGo rev-parse --short HEAD)
git config user.name "ADFA"
git config user.email "dev-team@appdevforall.org"
git add libs/
if git diff --cached --quiet; then
echo "No changes to libs/ — nothing to commit."
else
git commit -m "chore: update libs from CodeOnTheGo@${sha}"
git push
fi
echo "codeonthego_sha=${sha}" >> "$GITHUB_OUTPUT"
echo "head_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Stage .cgp files with website filenames
run: |
mkdir -p deploy-staging
# Discover every plugin module the same way scripts/update-libs.sh does
# (a sibling dir whose build.gradle.kts applies the plugin-builder Gradle
# plugin) so new plugins deploy to the website automatically. The website
# filename is the lowercased module name with any trailing "-plugin"
# stripped; the case arms hold the legacy filenames the website already
# links that don't follow that rule — never change an existing name.
# Keep in sync with SKIP_PLUGINS in scripts/update-libs.sh.
SKIP_PLUGINS=(pebble-custom-function-template-installer)
staged=0
for build_file in */build.gradle.kts; do
[ -f "$build_file" ] || continue
grep -qF "com.itsaky.androidide.plugins.build" "$build_file" || continue
module="$(dirname "$build_file")"
skip=0
for s in "${SKIP_PLUGINS[@]}"; do
[ "$s" = "$module" ] && skip=1 && break
done
if [ "$skip" -eq 1 ]; then
echo "Skipping $module (in SKIP_PLUGINS)"
continue
fi
case "$module" in
apk-viewer) name="apk-analyzer" ;;
markdown-preview) name="markdown-previewer" ;;
*) name="$(echo "${module%-plugin}" | tr '[:upper:]' '[:lower:]')" ;;
esac
src="$(ls "${module}/build/plugin/"*.cgp 2>/dev/null | grep -v -- '-debug\.cgp$' | head -n1)"
if [ -z "$src" ]; then
echo "ERROR: no release .cgp found under ${module}/build/plugin/ (did assemblePlugin run?)"
exit 1
fi
cp "$src" "deploy-staging/${name}.cgp"
echo "Staged $src -> deploy-staging/${name}.cgp"
staged=$((staged + 1))
done
if [ "$staged" -eq 0 ]; then
echo "ERROR: no plugin modules discovered under */build.gradle.kts"
exit 1
fi
ls -la deploy-staging/
- name: Upload .cgp artifacts for deploy job
uses: actions/upload-artifact@v4
with:
name: plugins-cgp
path: deploy-staging/*.cgp
retention-days: 7
if-no-files-found: error
- name: Publish release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.tag.outputs.name }}
target_commitish: ${{ steps.commit.outputs.head_sha }}
body: Built against CodeOnTheGo@${{ steps.commit.outputs.codeonthego_sha }}.
fail_on_unmatched_files: true
files: '*/build/plugin/*.cgp'
deploy:
needs: release
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Download .cgp artifacts
uses: actions/download-artifact@v4
with:
name: plugins-cgp
path: deploy-staging
- name: List staged files
run: ls -la deploy-staging/
- name: Set up SSH key
env:
GREENGEEKS_HOST: ${{ vars.GREENGEEKS_SSH_HOST }}
GREENGEEKS_KEY: ${{ secrets.GREENGEEKS_SSH_PRIVATE_KEY }}
GREENGEEKS_USER: ${{ vars.GREENGEEKS_SSH_USER }}
run: |
mkdir -p ~/.ssh
if [ -z "$GREENGEEKS_HOST" ]; then
echo "Error: GREENGEEKS_SSH_HOST variable is not set"
exit 1
fi
echo "$GREENGEEKS_KEY" > ~/.ssh/id_rsa
sed -i '$ { /^$/ d; }' ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
if ! grep -q "BEGIN.*PRIVATE KEY" ~/.ssh/id_rsa; then
echo "Error: SSH key does not appear to be in correct format"
exit 1
fi
cat > ~/.ssh/config <<EOF
Host *
IdentitiesOnly yes
PreferredAuthentications publickey
StrictHostKeyChecking no
UserKnownHostsFile ~/.ssh/known_hosts
PubkeyAuthentication yes
PasswordAuthentication no
Host $GREENGEEKS_HOST
User $GREENGEEKS_USER
IdentityFile ~/.ssh/id_rsa
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
ControlPersist 10m
ConnectTimeout 60
ServerAliveInterval 60
ServerAliveCountMax 10
EOF
chmod 600 ~/.ssh/config
unset SSH_AUTH_SOCK
unset SSH_AGENT_PID
ssh-keyscan -H "$GREENGEEKS_HOST" >> ~/.ssh/known_hosts 2>/dev/null
- name: Upload .cgp files via scp
env:
REMOTE: ${{ vars.GREENGEEKS_SSH_USER }}@${{ vars.GREENGEEKS_SSH_HOST }}
run: |
for f in deploy-staging/*.cgp; do
echo "Uploading $f -> $REMOTE:$PLUGINS_REMOTE_PATH/$(basename "$f")"
scp -o StrictHostKeyChecking=no "$f" "$REMOTE:$PLUGINS_REMOTE_PATH/"
done
- name: Verify remote MD5 checksums
env:
REMOTE: ${{ vars.GREENGEEKS_SSH_USER }}@${{ vars.GREENGEEKS_SSH_HOST }}
run: |
names=$(cd deploy-staging && ls *.cgp | tr '\n' ' ')
remote_sums=$(ssh "$REMOTE" "cd $PLUGINS_REMOTE_PATH && md5sum $names")
failures=0
while IFS= read -r line; do
remote_md5=$(echo "$line" | awk '{print $1}')
name=$(echo "$line" | awk '{print $2}')
local_md5=$(md5sum "deploy-staging/$name" | cut -d ' ' -f1)
if [ "$local_md5" = "$remote_md5" ]; then
echo "OK $name ($local_md5)"
else
echo "FAIL $name local=$local_md5 remote=$remote_md5"
failures=$((failures+1))
fi
done <<< "$remote_sums"
if [ $failures -gt 0 ]; then
echo "ERROR: $failures file(s) failed MD5 verification"
exit 1
fi
- name: Cleanup SSH key
if: always()
run: |
rm -f ~/.ssh/id_rsa ~/.ssh/known_hosts || true