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
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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=
7 changes: 7 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
34 changes: 31 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand All @@ -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.
85 changes: 53 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,40 @@
# IndopenSource.org

[![Deploy to GitHub Pages](https://github.com/IndopenSource/indopensource.org/actions/workflows/deploy-pages.yml/badge.svg)](https://github.com/IndopenSource/indopensource.org/actions/workflows/deploy-pages.yml)
[![CI](https://github.com/IndopenSource/indopensource.org/actions/workflows/ci.yml/badge.svg)](https://github.com/IndopenSource/indopensource.org/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](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
Expand All @@ -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`,
Expand All @@ -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

Expand All @@ -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

Expand Down
34 changes: 34 additions & 0 deletions docs/release-checklist.md
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions public/_redirects
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/sponsors/ /donasi/ 301
Binary file modified public/brand/apple-touch-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/brand/favicon-96.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/brand/favicon.ico
Binary file not shown.
Binary file added public/brand/tokengratis-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions scripts/sync-blog-posts.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -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.
Expand Down Expand Up @@ -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];
Comment thread
coderabbitai[bot] marked this conversation as resolved.

posts.push({
slug: slugFromPath(path),
Expand All @@ -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
});
}
Expand Down
Loading