Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/update-readme-deps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Update README dependencies

# Keeps the Appodeal dependency lists in README.md in sync with the live
# Dependencies Wizard API (URL from the APPODEAL_API_URL repository variable).
# The SDK version comes from package.json, so a version bump (push) refreshes the
# lists; the daily schedule catches adapter-version changes published between releases.

on:
workflow_dispatch: {}
push:
branches: [master]
paths:
- 'package.json'
- 'scripts/update-readme-deps.mjs'
schedule:
# Daily at 06:00 UTC.
- cron: '0 6 * * *'

permissions:
contents: write

concurrency:
group: update-readme-deps
cancel-in-progress: true

jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 20

- name: Regenerate README dependency lists
# URL comes only from the APPODEAL_API_URL repository variable
# (Settings → Secrets and variables → Actions → Variables). No hardcoded fallback —
# if the variable is unset the script fails loudly.
env:
APPODEAL_API_URL: ${{ vars.APPODEAL_API_URL }}
run: node scripts/update-readme-deps.mjs

- name: Commit changes if any
run: |
if git diff --quiet -- README.md; then
echo "README dependencies already up to date."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add README.md
git commit -m "chore: sync README dependency lists from Wizard API"
git push
38 changes: 19 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,31 +90,30 @@ yarn add react-native-appodeal
1. Go to `ios/` folder and open *Podfile*
2. Add Appodeal adapters. Add pods into `./ios/Podfile`:

<!-- appodeal-deps:ios:start -->
```ruby
source 'https://cdn.cocoapods.org'
platform :ios, '15.0'

Comment thread
da2gl marked this conversation as resolved.
source 'https://github.com/appodeal/CocoaPods.git'
source 'https://github.com/bidon-io/CocoaPods-Specs.git'

platform :ios, '13.0'
source 'https://cdn.cocoapods.org'

use_frameworks!

def appodeal
pod 'Appodeal', '4.1.0'
# Add adapter pods here.
# Use the Appodeal Dependencies Wizard to generate an up-to-date list:
# https://docs.appodeal.com/ios/advanced/configure-mediated-networks
pod 'Appodeal', '4.1.0'
end

target 'YourAppName' do
use_frameworks!
use_modular_headers!
appodeal
target 'Sample' do
project 'Sample/Sample.xcodeproj'
appodeal
use_modular_headers!
Comment on lines +107 to +110

config = use_native_modules!
use_react_native!(:path => config[:reactNativePath])
config = use_native_modules!
use_react_native!(:path => config[:reactNativePath])
end
```
<!-- appodeal-deps:ios:end -->

> [!TIP]
> Use the [Appodeal Dependencies Wizard](https://docs.appodeal.com/ios/advanced/configure-mediated-networks) to configure mediated networks and generate an always up-to-date dependency list for your Podfile.
Expand Down Expand Up @@ -195,16 +194,17 @@ To improve ad performance the following entries should be added:

Add dependencies into `android/app/build.gradle`
Comment thread
da2gl marked this conversation as resolved.

``` groovy
<!-- appodeal-deps:android:start -->
``` kotlin
repositories {
Comment on lines 195 to +199
maven { url = uri("https://artifactory.appodeal.com/appodeal") }
}
/* build.gradle.kts */
dependencies {
// ... other project dependencies
// Appodeal SDK 4.1.0
implementation("com.appodeal.ads.sdk:core:4.1.0")
// Add adapter dependencies here.
// Use the Appodeal Dependencies Wizard to generate an up-to-date list:
// https://docs.appodeal.com/android/advanced/configure-mediated-networks
}
```
<!-- appodeal-deps:android:end -->

Add repository into `android/build.gradle`

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"typecheck": "tsc",
"lint": "eslint \"**/*.{js,ts,tsx}\"",
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
"update-readme-deps": "node scripts/update-readme-deps.mjs",
"prepare": "bob build",
"release": "release-it --only-version"
},
Expand Down
145 changes: 145 additions & 0 deletions scripts/update-readme-deps.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#!/usr/bin/env node
// Updates the README.md dependency blocks from the Appodeal Wizard API.
// API output is used verbatim; only the iOS Podfile target gets RN linking injected.
Comment on lines +2 to +3

import { readFile, writeFile } from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';

// API base URL — required, supplied via the APPODEAL_API_URL env var (set by the
// GitHub Action / your shell). No fallback: fail loudly rather than hit a guessed host.
const API = (() => {
const raw = process.env.APPODEAL_API_URL;
if (!raw) {
throw new Error('APPODEAL_API_URL env var is required');
}
return raw.replace(/\/+$/, '');
})();
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
const README = join(ROOT, 'README.md');

// Category codes returned by the /sdks endpoint.
const CATEGORY_NETWORK = 2;
const CATEGORY_SERVICE = 3;

/** Read the single source-of-truth version from package.json. */
async function getVersion() {
const pkg = JSON.parse(await readFile(join(ROOT, 'package.json'), 'utf8'));
if (!pkg.version) throw new Error('package.json has no "version" field');
return pkg.version;
}

async function apiFetch(path, options = {}) {
const res = await fetch(`${API}${path}`, {
headers: { 'content-type': 'application/json', accept: '*/*' },
signal: AbortSignal.timeout(30_000),
...options,
});
if (!res.ok) {
throw new Error(`API ${path} -> HTTP ${res.status} ${res.statusText}`);
}
return res;
}

/** Collect every version id from a list of {versions:[{id}]} entries. */
function collectVersionIds(entries) {
return (entries ?? []).flatMap((e) => (e.versions ?? []).map((v) => v.id));
}

/** Run the 3-step recommended pipeline and return the rendered dependency text. */
async function fetchDependencyBlock(platform, version, lang) {
// Step 1 — recommended mediations.
const mediationsRes = await apiFetch(
`/v4/${platform}/${version}/mediations?recommended=true`
);
const mediations = collectVersionIds((await mediationsRes.json()).mediations);

// Step 2 — recommended sdks for those mediations, split into networks / services.
const sdksRes = await apiFetch(`/v4/${platform}/${version}/sdks?recommended=true`, {
method: 'POST',
body: JSON.stringify({ mediations, networks: [], services: [] }),
});
const sdks = (await sdksRes.json()).sdks ?? [];
const networks = collectVersionIds(sdks.filter((s) => s.category === CATEGORY_NETWORK));
const services = collectVersionIds(sdks.filter((s) => s.category === CATEGORY_SERVICE));

// Step 3 — render. iOS has no language suffix (always Ruby).
const path =
platform === 'ios'
? `/v4/${platform}/${version}/dependencies`
: `/v4/${platform}/${version}/dependencies/${lang}`;
const depsRes = await apiFetch(path, {
method: 'POST',
body: JSON.stringify({ mediations, networks, services }),
});
return (await depsRes.text()).replace(/\t/g, ' ');
}

/** Wrap rendered code in a fenced Markdown block. */
function fenced(lang, body) {
return `${lang}\n${body.trimEnd()}\n\`\`\``;
}

/**
* Inject the React Native linking lines into the iOS Podfile target. The Wizard renders
* a plain native target (`target 'Sample' do ... end`); RN apps additionally need the
* autolinking calls, otherwise the copied Podfile won't build. Everything else from the
* API response is left untouched.
*/
function addReactNativeLinking(podfile) {
const linking = [
'',
' use_modular_headers!',
'',
' config = use_native_modules!',
' use_react_native!(:path => config[:reactNativePath])',
].join('\n');
// The target block is the last one in the response; insert before its closing `end`.
const trimmed = podfile.trimEnd();
const patched = trimmed.replace(/\n[ \t]*end$/, `${linking}\nend`);
if (patched === trimmed) {
throw new Error('Could not locate the iOS Podfile target `end` to inject RN linking');
}
return patched;
}
Comment thread
da2gl marked this conversation as resolved.

/**
* Replace everything between the HTML-comment markers for `name` with `block`.
* Markers live OUTSIDE the fenced code block so they stay invisible in rendered Markdown:
* <!-- appodeal-deps:NAME:start ... --> ...block... <!-- appodeal-deps:NAME:end -->
*/
function replaceBetweenMarkers(readme, name, block) {
const startRe = new RegExp(`<!--\\s*appodeal-deps:${name}:start\\b[^>]*-->`);
const endRe = new RegExp(`<!--\\s*appodeal-deps:${name}:end\\s*-->`);
const startMatch = readme.match(startRe);
if (!startMatch) throw new Error(`Marker appodeal-deps:${name}:start not found in README.md`);
const startIdx = startMatch.index + startMatch[0].length;

const endMatch = readme.slice(startIdx).match(endRe);
if (!endMatch) throw new Error(`Marker appodeal-deps:${name}:end not found after start in README.md`);
const endIdx = startIdx + endMatch.index;

return `${readme.slice(0, startIdx)}\n${block}\n${readme.slice(endIdx)}`;
}

async function main() {
const version = await getVersion();
console.log(`Updating README dependency lists for Appodeal SDK ${version}`);

// android uses kts (`kt`) verbatim; ios ignores the language, returns Ruby, and gets
// the React Native linking injected into its target.
const android = await fetchDependencyBlock('android', version, 'kt');
const ios = addReactNativeLinking(await fetchDependencyBlock('ios', version));

let readme = await readFile(README, 'utf8');
readme = replaceBetweenMarkers(readme, 'android', fenced('``` kotlin', android));
readme = replaceBetweenMarkers(readme, 'ios', fenced('```ruby', ios));
Comment on lines +125 to +136

await writeFile(README, readme, 'utf8');
console.log('Done: README dependency blocks updated.');
}

main().catch((err) => {
console.error(`\n✖ ${err.message}`);
process.exit(1);
});