{tier.name}
+{tier.min}
+-
+ {tier.benefits.map((b) => (
+
- + + {b} + + ))} +
diff --git a/.env.example b/.env.example
index 9b23136..ad9d65d 100644
--- a/.env.example
+++ b/.env.example
@@ -24,3 +24,11 @@ GITHUB_TOKEN=
# Production and the deploy workflow use `/` (the site is served from the apex
# domain). Override only when previewing under a sub-path, e.g. `/my-fork/`.
ASTRO_BASE=/
+
+# Public analytics identifiers injected at build time. These values are visible
+# in the generated HTML/browser and are identifiers, not secrets.
+PUBLIC_CLOUDFLARE_WEB_ANALYTICS_TOKEN=
+PUBLIC_GOOGLE_ANALYTICS_ID=
+
+# Optional Google Search Console HTML-tag verification value.
+PUBLIC_GOOGLE_SITE_VERIFICATION=
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 85ffb02..841b490 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -4,7 +4,14 @@
## Validation
+- [ ] `npm test`
- [ ] `npm run build`
+- [ ] Cloudflare branch preview checked on desktop and mobile
+
+## Content checks
+
+- [ ] Claims, organizations, sponsors, and metrics have a verifiable source
+- [ ] New internal links work with `ASTRO_BASE`
## Notes
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9f701b9..125625e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -6,13 +6,13 @@ Thanks for helping improve IndopenSource.org.
```bash
npm install
-npm run sync:projects
npm run dev
```
Before opening a pull request:
```bash
+npm test
npm run build
```
@@ -35,13 +35,41 @@ Use `GITHUB_TOKEN` or `GH_TOKEN` to avoid low GitHub API rate limits.
`main` commit to production.
- Keep changes focused.
- Prefer Astro components in `src/components` over large page-only markup.
-- Keep internal links compatible with GitHub Pages base paths by using `withBase()`.
-- Run `npm run build` and include the result in the PR description.
+- Use `withBase()` for internal links that must remain valid when `ASTRO_BASE`
+ is set by CI or a preview environment.
+- Run `npm test` and `npm run build`, then include the results in the PR description.
- For content or roadmap changes, link the relevant issue or discussion.
+## Community Directory
+
+The Community page reads `src/data/communities.json`. To propose one community,
+add one object containing:
+
+- `name`
+- `description`
+- `url` pointing to an official channel
+- optional `focus`
+- optional `location`
+
+Keep one community per pull request and explain your relationship to it or add a
+source that reviewers can verify. Do not add inferred or inactive communities.
+
+## Content Quality
+
+- Lead with a direct summary before background detail.
+- Cite primary sources for factual claims and include the relevant date.
+- Use descriptive headings and stable internal links to related pages.
+- Identify authors and keep publication and update dates traceable.
+- Do not add speculative facts, generated statistics, or special AI-search
+ markup that is not supported by search-engine documentation.
+
## Production Release
The `release` branch is reserved for Cloudflare production deployments. Only
IndopenSource organization members may update it, through a pull request from
`main` that passes CI. Merge production promotion pull requests with a merge
commit so `main` remains in the `release` history.
+
+Cloudflare creates a branch preview for contribution pull requests. Review that
+preview before promoting `main` to `release`; use
+[`docs/release-checklist.md`](docs/release-checklist.md) for the production pass.
diff --git a/README.md b/README.md
index ddba074..4956d18 100644
--- a/README.md
+++ b/README.md
@@ -1,35 +1,40 @@
# IndopenSource.org
-[](https://github.com/IndopenSource/indopensource.org/actions/workflows/deploy-pages.yml)
+[](https://github.com/IndopenSource/indopensource.org/actions/workflows/ci.yml)
[](LICENSE)
-Website roadmap untuk `https://indopensource.org`.
+Website komunitas dan direktori open source Indonesia di `https://indopensource.org`.
## Tech Stack
- Astro
- Tailwind CSS
- TypeScript
-- GitHub Pages
- GitHub Actions
+- Cloudflare Workers Static Assets
## Pages
-- Home: ringkasan roadmap dan arah website.
+- Home: pintu masuk ke direktori, roadmap, pembelajaran, dan kontribusi.
- Falsafah: prinsip komunitas dan kurasi open source Indonesia.
-- Projects: direktori dari `IndopenSource/awesome-indonesia`.
-- Blog: placeholder untuk repo khusus `IndopenSource/Blog-IndopenSource`.
-- Forum: rencana integrasi `https://github.com/orgs/IndopenSource/discussions`.
-- Contact: kanal GitHub organization, projects, dan discussions.
+- Projects: direktori proyek dari `IndopenSource/awesome-indonesia`.
+- Top User: peringkat pemilik repository berdasarkan akumulasi stars.
+- Komunitas: direktori komunitas yang diusulkan melalui pull request.
+- Roadmap: rencana kerja publik 2026–2027.
+- Belajar dan Blog: materi serta artikel yang disinkronkan dari repository sumber.
+- Donasi: informasi OpenSawer, sponsorship, CSR, dan dukungan non-dana.
+- Forum dan Kontak: GitHub Discussions serta kanal resmi IndopenSource.
## Development
```bash
npm install
-npm run sync:projects
npm run dev
```
+Sinkronisasi data bersifat opsional untuk pengembangan UI. File hasil sinkronisasi
+yang diperlukan sudah tersedia di `src/data/`.
+
## Project Structure
```txt
@@ -45,38 +50,36 @@ src/
## Checks
```bash
+npm test
npm run check
npm run build
```
-## MVP Pre-release
-
-MVP awal berfokus pada homepage sebagai pintu masuk roadmap IndopenSource:
-
-- Home menjelaskan posisi `indopensource.org`.
-- Projects sudah punya data awal dari `awesome-indonesia`.
-- Falsafah, Blog, Forum, dan Contact tersedia sebagai halaman roadmap.
-- Deployment memakai GitHub Pages bawaan repo lewat GitHub Actions.
-
-Rilis pre-release bisa dibuat dari tag `v0.1.0-mvp` setelah workflow Pages hijau.
-URL produksi setelah custom domain aktif adalah `https://indopensource.org/`.
-
## Deployment
-Deployment saat ini masih memakai GitHub Pages melalui workflow
-`.github/workflows/deploy-pages.yml`. Cloudflare production berikutnya akan
-dibangun dari branch `release`.
+Cloudflare terhubung langsung ke repository dan membuat preview untuk pull
+request atau branch non-production. Production hanya dibangun dari branch
+`release`.
- Build command: `npm run build`
+- Deploy command: `npx wrangler deploy`
- Output directory: `dist`
-- Source: GitHub Actions
+- Konfigurasi Worker: `wrangler.jsonc`
+- Production branch: `release`
- Custom domain: `indopensource.org`
-- Pages base path: `/`
-Semua kontribusi tetap masuk ke `main`. Rilis produksi dipromosikan oleh anggota
-organisasi melalui pull request dari `main` ke `release`; jangan membuka pull
+Semua kontribusi masuk ke `main`. Anggota organisasi mempromosikan versi yang
+sudah diuji melalui pull request dari `main` ke `release`; jangan membuka pull
request kontribusi langsung ke `release`.
+Workflow GitHub Pages lama masih berada di `.github/workflows/deploy-pages.yml`
+dan bukan sumber deployment production Cloudflare. Putuskan penghapusan atau
+perannya sebagai fallback sebelum release publik agar tidak ada dua jalur deploy
+yang dianggap aktif.
+
+Lihat [checklist release](docs/release-checklist.md) sebelum mempromosikan
+`main` ke `release`.
+
## Project Sync
`npm run sync:projects` membaca `repos.json` dari `IndopenSource/awesome-indonesia`,
@@ -88,8 +91,14 @@ Gunakan `GITHUB_TOKEN` atau `GH_TOKEN` untuk rate limit yang lebih lega.
## Blog Sync
`npm run sync:blog` membaca artikel Markdown dari
-`IndopenSource/Blog-IndopenSource`, mengambil metadata commit penulis/rilis,
-dan menulis hasilnya ke `src/data/blog-posts.json`.
+`IndopenSource/Blog-IndopenSource`, mengambil tanggal commit pertama, commit
+terbaru, dan seluruh kontributor unik setiap artikel, lalu menulis hasilnya ke
+`src/data/blog-posts.json`. Halaman artikel menautkan commit terbaru tersebut
+agar riwayat sumber dapat diperiksa langsung.
+
+Artikel berstatus `draft` tetap dibangun pada URL `/blog/preview/{slug}/`,
+tetapi tidak ditampilkan di indeks blog, tidak masuk sitemap, dan memakai
+`noindex, nofollow, noarchive` sampai statusnya diterbitkan.
## Auto Sync
@@ -103,8 +112,20 @@ Workflow `.github/workflows/sync-content.yml` memperbarui data secara otomatis.
- `sync-content`
Jika data berubah, workflow membuat commit `Sync content data`. Setelah commit
-masuk `main`, workflow yang sama akan upload artifact dan deploy ulang GitHub
-Pages agar perubahan data langsung tayang.
+masuk `main`, perubahan mengikuti validasi dan alur promosi `main` ke `release`.
+Workflow ini masih memuat langkah deployment GitHub Pages lama; jangan menganggap
+hasil tersebut sebagai production Cloudflare.
+
+## Analytics & Search Verification
+
+Analytics aktif saat build Cloudflare menerima environment variable berikut:
+
+- `PUBLIC_CLOUDFLARE_WEB_ANALYTICS_TOKEN`
+- `PUBLIC_GOOGLE_ANALYTICS_ID` (format `G-XXXXXXXXXX`)
+- `PUBLIC_GOOGLE_SITE_VERIFICATION` (opsional)
+
+Nilainya merupakan identifier publik dan tidak perlu dimasukkan ke Git. Isi di
+pengaturan build Cloudflare untuk production dan preview sesuai kebutuhan.
## Contributing
diff --git a/docs/release-checklist.md b/docs/release-checklist.md
new file mode 100644
index 0000000..3bd76ba
--- /dev/null
+++ b/docs/release-checklist.md
@@ -0,0 +1,34 @@
+# Release checklist
+
+Production is promoted from `main` to `release` through a pull request opened by
+an IndopenSource organization member.
+
+## Before promotion
+
+- [ ] Contribution pull requests are merged into `main` and CI is green.
+- [ ] Cloudflare branch preview has been checked on desktop and mobile.
+- [ ] `npm test` and `npm run build` pass from the intended release commit.
+- [ ] Navigation, project detail pages, `/donasi/`, `/komunitas/`, `/roadmap/`,
+ `/sitemap.xml`, and the `/sponsors/` redirect have been checked.
+- [ ] `admin@`, `press@`, `sponsor@`, and `legal@indopensource.org` can receive
+ mail, or links to inactive addresses have been removed.
+- [ ] DNS, custom domain, TLS, and the Cloudflare production branch are correct.
+- [ ] Cloudflare build variables for the Web Analytics token and Google
+ Analytics Measurement ID are configured.
+- [ ] The role of the legacy GitHub Pages workflows has been decided.
+
+## Promote
+
+1. Open a pull request from `main` to `release`.
+2. Require green CI and review the diff for unexpected generated data.
+3. Merge with a merge commit so `main` remains in the `release` history.
+4. Wait for the Cloudflare production deployment to pass.
+
+## After deployment
+
+- [ ] Smoke-test `https://indopensource.org/` and the primary navigation.
+- [ ] Confirm canonical URLs, robots, sitemap, favicon, and social previews.
+- [ ] Confirm Cloudflare Web Analytics and Google Analytics receive a test page
+ view without CSP errors.
+- [ ] Send a test message to each published email address.
+- [ ] Record the production commit or tag as the rollback point.
diff --git a/public/_redirects b/public/_redirects
new file mode 100644
index 0000000..54b9661
--- /dev/null
+++ b/public/_redirects
@@ -0,0 +1 @@
+/sponsors/ /donasi/ 301
diff --git a/public/brand/apple-touch-icon.png b/public/brand/apple-touch-icon.png
index e36db39..b50d69a 100644
Binary files a/public/brand/apple-touch-icon.png and b/public/brand/apple-touch-icon.png differ
diff --git a/public/brand/favicon-96.png b/public/brand/favicon-96.png
new file mode 100644
index 0000000..4c9183a
Binary files /dev/null and b/public/brand/favicon-96.png differ
diff --git a/public/brand/favicon.ico b/public/brand/favicon.ico
index 815d094..29eb739 100644
Binary files a/public/brand/favicon.ico and b/public/brand/favicon.ico differ
diff --git a/public/brand/tokengratis-icon.png b/public/brand/tokengratis-icon.png
new file mode 100644
index 0000000..bfebf7c
Binary files /dev/null and b/public/brand/tokengratis-icon.png differ
diff --git a/scripts/sync-blog-posts.mjs b/scripts/sync-blog-posts.mjs
index 661eaac..cc2953d 100644
--- a/scripts/sync-blog-posts.mjs
+++ b/scripts/sync-blog-posts.mjs
@@ -161,6 +161,20 @@ async function getCommitMeta(path) {
const firstCommit = commits.at(-1);
const latestCommit = commits.at(0);
const author = firstCommit?.author;
+ const authors = [];
+ const seenAuthors = new Set();
+
+ for (const item of commits.toReversed()) {
+ const name = item.author?.login || item.commit?.author?.name;
+ if (!name || seenAuthors.has(name.toLowerCase())) continue;
+ seenAuthors.add(name.toLowerCase());
+ authors.push({
+ name,
+ avatarUrl: item.author?.avatar_url || '',
+ url: item.author?.html_url || '',
+ committedAt: item.commit?.author?.date || ''
+ });
+ }
return {
author: {
@@ -169,6 +183,9 @@ async function getCommitMeta(path) {
url: author?.html_url || firstCommit?.html_url || `https://github.com/${BLOG_REPO}`,
committedAt: firstCommit?.commit?.author?.date || ''
},
+ authors,
+ latestCommitSha: latestCommit?.sha || '',
+ latestCommitUrl: latestCommit?.html_url || '',
// The latest commit timestamp is the date of the most recent EDIT, not a
// publication/release event (CC-9). Name it honestly as "last modified" so
// the rendered label can stop calling an edit timestamp a release date.
@@ -244,6 +261,12 @@ for (const path of articleFiles) {
const commitMeta = await getCommitMeta(path);
const editorialDate = (data.date || '').trim();
const resolvedAuthor = resolveAuthor(commitMeta.author, data.authors);
+ const resolvedAuthors = resolvedAuthor.fromFrontmatter
+ ? [
+ resolvedAuthor.author,
+ ...commitMeta.authors.filter((author) => author.name.toLowerCase() !== resolvedAuthor.author.name.toLowerCase())
+ ]
+ : commitMeta.authors.length ? commitMeta.authors : [resolvedAuthor.author];
posts.push({
slug: slugFromPath(path),
@@ -264,7 +287,10 @@ for (const path of articleFiles) {
// edit timestamp separately for provenance (CC-9).
releasedAt: editorialDate || commitMeta.author.committedAt || commitMeta.lastModifiedAt,
lastModifiedAt: commitMeta.lastModifiedAt,
+ latestCommitSha: commitMeta.latestCommitSha,
+ latestCommitUrl: commitMeta.latestCommitUrl,
author: resolvedAuthor.author,
+ authors: resolvedAuthors,
authorFromFrontmatter: resolvedAuthor.fromFrontmatter
});
}
diff --git a/src/components/DonationPage.astro b/src/components/DonationPage.astro
new file mode 100644
index 0000000..752b9db
--- /dev/null
+++ b/src/components/DonationPage.astro
@@ -0,0 +1,254 @@
+---
+import BaseLayout from '../layouts/BaseLayout.astro';
+import BaseButton from './BaseButton.astro';
+import PageHeader from './PageHeader.astro';
+import { withBase } from '../lib/urls';
+
+const tiers = [
+ {
+ name: 'Utama',
+ min: 'Rp 5.000.000/tahun',
+ benefits: [
+ 'Logo di homepage & footer website',
+ 'Profil organisasi di halaman sponsor',
+ 'Kolaborasi satu program atau event',
+ 'Laporan penggunaan dan dampak berkala'
+ ],
+ highlight: true
+ },
+ {
+ name: 'Partner',
+ min: 'Rp 2.500.000/tahun',
+ benefits: [
+ 'Logo di halaman sponsor & footer',
+ 'Apresiasi di kanal komunitas',
+ 'Laporan penggunaan dana berkala'
+ ],
+ highlight: false
+ },
+ {
+ name: 'Pendukung',
+ min: 'Rp 1.000.000/tahun',
+ benefits: [
+ 'Logo di halaman sponsor',
+ 'Apresiasi publik',
+ 'Akses laporan terbuka'
+ ],
+ highlight: false
+ },
+ {
+ name: 'Sahabat',
+ min: 'Rp 250.000/tahun',
+ benefits: [
+ 'Nama di halaman sponsor',
+ 'Apresiasi publik',
+ 'Akses laporan terbuka'
+ ],
+ highlight: false
+ }
+];
+
+const currentSponsors = [
+ { name: 'Tokengratis.id', since: '2026', url: 'https://tokengratis.id/', logo: withBase('/brand/tokengratis-icon.png') }
+];
+
+const openSawerIcon = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='64'%20height='64'%20viewBox='0%200%2040%2040'%3e%3ctitle%3eOpenSawer%3c/title%3e%3ccircle%20cx='20'%20cy='20'%20r='20'%20fill='%230f694e'/%3e%3ccircle%20cx='20'%20cy='11.5'%20r='3.2'%20fill='%23e8b83f'/%3e%3cpath%20d='M10.5%2019.5c1.4%207.2%205%2010.8%209.5%2010.8s8.1-3.6%209.5-10.8M12%2019.5h16'%20fill='none'%20stroke='%23fff'%20stroke-width='3.2'%20stroke-linecap='round'/%3e%3c/svg%3e";
+---
+
+ IndopenSource dijalankan oleh komunitas sukarela. Dukungan individu, sponsorship, CSR, dan kolaborasi membantu kami merawat infrastruktur, mengadakan event, dan mendukung proyek-proyek potensial. OpenSawer akan dihubungkan ke halaman setiap proyek yang terdaftar di direktori IndopenSource. Setelah integrasinya siap, siapa pun dapat memilih proyek lalu berdonasi langsung melalui kanal OpenSawer proyek tersebut. Lihat kanal donasi terbuka yang sedang disiapkan untuk komunitas dan proyek. Setiap kontribusi membantu menjaga open source Indonesia tetap hidup dan berkembang.
+ Dana CSR dapat diarahkan untuk infrastruktur komunitas, dokumentasi dan materi belajar,
+ event, serta dukungan bagi proyek open source Indonesia yang membutuhkan ruang tumbuh.
+ Program dan indikator hasil disepakati bersama perusahaan sebelum pelaksanaan.
+
+ Pasal 74 UU No. 40 Tahun 2007
+ mewajibkan Perseroan yang menjalankan usaha di bidang dan/atau berkaitan dengan sumber daya alam melaksanakan Tanggung Jawab Sosial dan Lingkungan. Anggarannya diperhitungkan sebagai biaya Perseroan dengan memperhatikan kepatutan dan kewajaran.
+
+ PP No. 47 Tahun 2012
+ mengatur pelaksanaannya melalui rencana kerja tahunan dan pemuatan hasil pelaksanaan dalam laporan tahunan Perseroan.
+
+ Pasal 15 huruf b UU No. 25 Tahun 2007
+ juga mewajibkan setiap penanam modal melaksanakan tanggung jawab sosial perusahaan. Penerapan kewajiban tetap mengikuti status, bidang usaha, dan aturan sektoral perusahaan masing-masing.
+
+ Penerimaan dan penggunaan dana CSR direncanakan dipublikasikan melalui OpenSawer. Laporan program,
+ rincian penyaluran, bukti kegiatan, dan pembaruan dampak akan dibuka setelah kanal pelaporan siap.
+ Lihat aplikasi open source yang akan digunakan untuk membuka catatan dana dan laporan program. Terima kasih kepada organisasi yang telah mendukung misi IndopenSource. Mendukung sejak {s.since} Bantu memperluas jangkauan, merawat proyek, atau membuka peluang kolaborasi bersama komunitas. Ajukan program, event, materi belajar, atau dukungan ke proyek potensial. Follow organisasi, star repository yang berguna, dan pantau perkembangannya. Follow media sosial dan bagikan proyek atau kegiatan yang relevan. Bawa ide, kebutuhan proyek, dokumentasi, review, atau bantuan teknis. Semua paket memberikan akses ke laporan dampak dan apresiasi publik. {tier.min} Kami terbuka untuk paket kustom, sponsorship event, atau bentuk kolaborasi lain yang sejalan dengan misi open source.Dukung proyek yang Anda gunakan.
+
+
Buka OpenSawer
+ Kami menerima dukungan CSR.
+ Dasar hukum dan cakupannya
+ Dana masuk, penggunaan, dan dampaknya dapat dilihat.
+
+
+
OpenSawer
+
+ Organisasi yang mendukung.
+
+
{s.name}
+ Dukungan tidak selalu berupa uang.
+ Kolaborasi
+ Dukung di GitHub
+ Ikuti & bagikan
+ Ikut berkontribusi
+ Pilih tingkat kemitraan.
+ {tier.name}
+
+ {tier.benefits.map((b) => (
+
+ Mari diskusikan kemitraan Anda.
+
- Rumah terbuka untuk merawat daftar proyek, mendokumentasikan falsafah, - menulis pembelajaran, dan menghubungkan kontributor open source Indonesia. + Rumah terbuka untuk merawat daftar proyek, menulis pembelajaran, + dan menghubungkan kontributor open source Indonesia.