spanezz / staticsite Goto Github PK
View Code? Open in Web Editor NEWStatic site generator
License: GNU General Public License v3.0
Static site generator
License: GNU General Public License v3.0
(from a conversation with @DonKult)
staticsite currently does not set a locale on startup, meaning that locale-dependent contents like days in date formatting, default to the "C" locale.
It would make sense to add someting like Django's LANGUAGE_CODE to the settings, and have staticsite activate that locale on startup.
This wouldn't help create multilanguage sites, but it would help in creating sites in language different than English.
Following the 1st example for blog creations if I do:
apt install --install-recommends /srv/scratch/Downloads/staticsite_2.2-1_all.deb
mkdir ssite
cd ssite
edit index.md
ssite build -o built_site
ssite show
Traceback (most recent call last):
File "/usr/bin/ssite", line 33, in
sys.exit(main())
File "/usr/bin/ssite", line 27, in main
return handler.run()
File "/usr/lib/python3/dist-packages/staticsite/cmd/serve/init.py", line 58, in run
self.start_server()
File "/usr/lib/python3/dist-packages/staticsite/cmd/serve/init.py", line 41, in start_server
app.reload()
File "/usr/lib/python3/dist-packages/staticsite/cmd/serve/server.py", line 176, in reload
self.site.load()
File "/usr/lib/python3/dist-packages/staticsite/site.py", line 523, in load
self._crossreference()
File "/usr/lib/python3/dist-packages/staticsite/site.py", line 556, in _crossreference
feature.crossreference()
File "/usr/lib/python3/dist-packages/staticsite/features/images.py", line 232, in crossreference
if not page.image and basename_no_ext(page.src.relpath) == name:
AttributeError: 'Asset' object has no attribute 'image'
if I remove the built_site directory instead it works.
After introducing the concept of features, it should be quite straightforward to add a code
feature that renders files with extension .py
, .c
, .cc
, and so on, as syntax highlighted code, with line numbers, anchors for line numbers, and so on.
If a page includes an inline image foo/bar.jpg
, its syndication feed will also link it at foo/bar.jpg
, even if the syndication page is in another path in the site, breaking the relative link to the image.
With the upgrade to bullseye, the packaged bootstrap assets contain broken symlinks. Building a site with it fails:
$ ssite build …
Traceback (most recent call last):
File "/usr/bin/ssite", line 54, in <module>
main()
File "/usr/bin/ssite", line 46, in main
res = handler.run()
File "/usr/lib/python3/dist-packages/staticsite/cmd/build.py", line 21, in run
site = self.load_site()
File "/usr/lib/python3/dist-packages/staticsite/cmd/command.py", line 61, in load_site
site.load()
File "/usr/lib/python3/dist-packages/staticsite/site.py", line 356, in load
self.scan_content()
File "/usr/lib/python3/dist-packages/staticsite/site.py", line 318, in scan_content
self.theme.scan_assets()
File "/usr/lib/python3/dist-packages/staticsite/theme.py", line 305, in scan_assets
self.site.scan_tree(
File "/usr/lib/python3/dist-packages/staticsite/site.py", line 332, in scan_tree
root.scan(dir_fd)
File "/usr/lib/python3/dist-packages/staticsite/contents.py", line 24, in wrapper
return f(self, dir_fd)
File "/usr/lib/python3/dist-packages/staticsite/contents.py", line 330, in scan
subdir.scan(subdir_fd)
File "/usr/lib/python3/dist-packages/staticsite/contents.py", line 24, in wrapper
return f(self, dir_fd)
File "/usr/lib/python3/dist-packages/staticsite/contents.py", line 316, in scan
self.files[entry.name] = file.File.from_dir_entry(self.src, entry)
File "/usr/lib/python3/dist-packages/staticsite/file.py", line 36, in from_dir_entry
stat=entry.stat())
FileNotFoundError: [Errno 2] No such file or directory: 'bootstrap-grid.css.map'
We need handling of broken symlinks :/
Python 3.9 introduced graphlib.TopologicalSorter.
Staticsite could use it by default, and fallback to the internal implementation (currently in staticsite/toposort.py
) for older Python versions that don't have it in the standard library.
It's also worth refactoring toposort
to match the API of graphlib.TopologicalSorter
, turning it into a polyfill kind of thing.
Currently, settings
in staticsite/core.py
is a singleton, which makes unittest using different configuration a bit awkward. I have a simple workaround in my branch, so this is a minor issue.
Currently staticsite show/serve don't trigger a reload when staticsite's code changes.
/usr/lib/python3/dist-packages/tornado/autoreload.py:_reload_on_update
can be a good example of how to list source locations for the current program.
For serve, just doing an exec
of sys.argv
would be enough. For show
, this would cause the new process to bind to a different port, unless --port
was used.
Alternatively, we can keep the server socket open and let the new process inherit and use it.
Add an HOWTO on how to add a blog to a site
Add a DEBUG
setting, similar to Django, which if enabled adds a popup with developer information to the base template, with dump_meta
information for the page.
Set DEBUG=True automatically on ssite serve
.
Creating a package in a rpm-based distro from the setup.py
is pretty straightforward and could be mentioned somewhere in the wiki:
python3 setup.py bdist_rpm --requires="python3-inotify python3-markdown python3-docutils python3-jinja2 python3-pytz python3-dateutil python3-pyyaml python3-pillow"
Reference documentation:
https://docs.python.org/3/distutils/builtdist.html#creating-rpm-packages
One of the best features of staticsite is that it has documentation! Sadly the new Features feature isn't really documented apart from being mentioned in NEWS and of course in code.
Would be nice if an example hello-world Feature could be added so a non-native python-speaker has an easier time understanding how to set it up. Figuring out that a FEATURES
dict is required and the content thereof wasn't super hard but could really be easier…
(Also, shouldn't staticsite skip modules without a FEATURES
attribute given it just crashes later instead of just printing a warning?)
It would be nice if staticsite could be configured to read the fallback page date from git log instead of the filesystem, because the latter is easily made inaccurate by actions such as moving the repository around, push/pull at a different time, etc.
ssite serve
will read settings.py
or .staticsite.py
files and execute them. This means that if it is used to preview random git repositories, a repository with such a file can get it executed. This can be both a security issue and an annoyance, as in for example previewing a django project.
Document that ssite show
does not have this issue and should be used by default. ssite serve
is still useful to serve a site that the user has control of.
ssite serve
can be used out of the box for live previewing documentation on github/gitlab repositories.
This can go in a little HOWTO.
(from a conversation with @DonKult)
In the example you have a dropdown menu which is nice, but I would like
to link to a page showing all entries of the series, which is the page
of the associated tag, but reaching that seems hard:
url_for(taxonomy('tags').categories[page.meta.series])
I was kinda hoping forurl_for(page.meta.series_tag)
especially if
– not that I have tried – you have multiple taxonomies with series's…
If the series is autogenerated from a tag, indeed a way to generate a
link to the tag page would be enough. If the series is not
autogenerated from a tag, we currently wouldn't have a recap page for
the series.For the first case, the series feature could easily add a metadata to
the page pointing to the series tag.For the second case, I'm thinking of mandating that series have to be
tags. One can always create a 'series' taxonomy, if one doesn't want to
add some series to the normal set of tags. This would also automatically
provide each series with an RSS feed, and maybe simplify some of the
series code, by just using tags code to track which pages belong to a
series.
TL;DR: merge series and taxonomies, mandating the fact that a series is an entry in a taxonomy.
The series
header can be used to both add a tag and state that it is used as a series.
Document that if one doesn't want to mix series in one's own tag, one can create a new taxonomy for series. That would also give an index pages to all the series in a website.
Currently the .j2.
marker in file names is used to distinguish static assets from jinja2 templates.
Now that we have directory metadata, we potentially have a way to tell the j2 feature a list or glob of files to read as templates regardless of their name.
For example, a .staticsite
file could have a way to pass information to features:
features:
j2:
files: [*.html]
this seems rather verbose. Alternatively, allow files
to be a glob, and use its metadata to communicate with features:
files:
*.html:
feature: jinja2
this sounds flexible but not really straightforward. Another option can be to say that if one provides extra metadata for a .html
file, then the j2 feature would automatically consider it a template:
files:
index.html:
title: "Enrico's interesting site"
And if in the future this causes static assets to be wrongly interpreted as templates, one could easily add a static: yes
metadata to disable it on a file by file basis.
If a blog page has an image, it should show, maybe scaled small, in the blog aggregation.
#39 has useful links for responsive images
In example/demo/content/tags.taxonomy
the template title & description of the archive use page.name
and the refdoc documents its existence, but it seems to have disappeared at some point (I am at least reasonably sure it existed last year).
Adding to ArchivePage
in staticsite/features/syndication.py
(I would have kinda expected it to be in taxonomy.py
) a to_dict
method "stolen" from the CategoryPage and self.name = self.created_from.to_dict().get("name")
to the constructor solves this small problem, but I fully expect there to be a better/cleaner way.
Related: I see that lib/blog.html
template uses page.created_from.…
, so perhaps that should be used in the templates instead of page.name
, but it isn't documented.
I wonder if you would be interested in a "GitHub mode".
Usecase: When publishing a software project on GitHub, the README.md is the main entry point into the documentation, maybe linking other markdown documents in the repository. It should be easy to turn this structure into a nice homepage for the project (and push it to the gh-pages branch).
Modification needed:
If you think something like this sounds useful, I'll start working on it...
Allow to specify in a .staticsite
file that the directory and all its contents are to be treated a single page for the site, copied verbatim, linkable by root path only, and not scanned further for other pages
Ideas for implementing incremental rendering:
os.normpath
) to core.Page objects. This is needed both to render in-memory for ssite serve
and for comparing what should be rendered with the existing contents of web/
git log
(or GitPython) to see which pages have changed since the last run.
(from a conversation with @DonKult)
One could add an image
feature that picks images from the content directory, scans EXIF tags for metadata, and depending on image sizes, generates scaled down versions and thumbnails.
One could also skip doing the scaling if the destination file already exists in the output directory and does not have an older mtime.
Finally, hook into the link rewriter, and when seing an <img>
tag with a src
to an image page, it
can turn it into a <picture>
tag instead, properly linking to a set of resolutions.
Hello,
is there a way to extend staticsite
with custom commands (corresponding to directives)? I know that I could write a markdown extension, but I am not sure that the code of this extension would be passed the staticsite "context" (e.g. setting and page variables).
-- Louis
Ideas for multilanguage support, based on a conversation with @DonKult.
The text type (Locale::Po4a::Text) has partial markdown support. The support isn't perfect, but was good enough so far to translate a bunch of paragraphs more or less easily.
Maybe it's enough to play with multilanguage staticsite prototypes, and see where they'd lead.
Example invocations:
update-po: Impressum.md
po4a-updatepo -M utf-8 -f text -o markdown -o tabs=verbatim -m Impressum.md -p po/en.po
translate-files-en: po/en.po
po4a-translate -M utf-8 -f text -k 0 -o markdown -m Impressum.md -p $^ -l Imprint.md
Currently, only explicit interlinking of pages across different languages has been tested.
This bit in an html header would look nice, but it doesn't seem like mainstream browsers currently make use of that:
{%- if page.meta.hreflang -%}
<link rel="alternate" hreflang="x-default" href="{{ url_for(page) }}" />
<link rel="alternate" hreflang="{{ page.meta.content_lang }}" href="{{ url_for(page) }}" />
{%- for reflang in page.meta.hreflang -%}
<link rel="alternate" hreflang="{{ reflang[0] }}" href="{{ url_for(reflang[1]) }}" />
{%- endfor -%}
It's worth looking how lektor does it, since it claim multilingual support.
For convenience, two links to doing language content negotiation with apache and nginx:
Generating site pages for one language would also require a setlocale
call to generate things like dates correctly. See #22
Add a 'syndication date' header that would only add the page to syndication after the given date, instead of after the page meta.date
.
This can be used to distinguish pages still in draft, not added to the site, and pages added to the site,
but syndicated at a later date. It would allow, for example, to publish a full series in one go, so people who get interested don't get a partial story and can binge-read, and syndicate it in instalments over several days, for people who prefer bite-sized pieces in their feed reader.
It would be interesting to allow to configure an add_to
filter for one or more categories in a taxonomy. This would allow some kind of auto-tagging, like adding a blog
series
category to all pages in blog/*
.
dump_meta now writes data in human readable and parsable yaml/json/toml.
However, pages don't yet contribute to the metadata the extra information they possess beyond their metadata, like name
for taxonomy category pages, or the list of syndicated pages for syndication page.
There's a need to do a review of all Page
subclasses, and tweak their to_dict
method to make sure its result has all that templates can use from the page.
Doing this could very likely result in a change of the structure of the data output by dump_meta, to have it match what a template can see. For example, page metadata should end up in a meta
dict instead of being at top level.
(from a conversation with @DonKult)
Related: I had hoped the doc would tell me what is a sensible choice as
a dependency. The hello feature is simple enough and the finalize
comment explains the dependencies, but I think that deserves some named
entry points so my "tags-ng" feature can be put before
"_autogenerated_pages" and "hello" after it.
And without the finalize, what would be a good dependency then?
I like the idea of well known synchronization points, like systemd kind of does with .target units. I can think of "contents loaded" and "autogenerated pages" as two useful such points.
A metadata defining an absolute path in site_path
, can cause the rendered file to be written to arbitrary locations in the file system, due to the behaviour of os.path.join
with absolute paths.
Make sure we test that leading slashes are stripped from site_path
when processing pages, so that the generated build_path
entries are all relative path.
Hi,
I was puzzled for a while testing staticsite as as soon as I was modifying an asset like a CSS file it would disappear from the build and no amount of revert fixed that…
Turns out that with some debugging after touching an asset file staticsite believes that asset is from the future and not including it as a draft… That is caused by code in staticsite/asset.py
which assumes that the modification time of a file is UTC and should be localized, while it is 'badly' localized already, just without a timezone… I worked around it locally with dt = datetime.datetime.fromtimestamp(os.path.getmtime(self.src_abspath), pytz.timezone(settings.TIMEZONE))
which works, but depending on the timezone setting is likely not what staticsite should do here.
Disable symlinks in content dir unless allowed in settings.py.
(from a conversation with @DonKult)
The sha256 is for the script-tag livereload inserts. I could just leave
that in for production, but sometimes it would be handy to know if we
are 'build', 'serve'd or perhaps even 'show'n.Well, super-ideally livereload would apply that themself although that
could become complicated really fast on less static sites.
Oh, interesting problem, that. I haven't yet gained CSP as a habit, shame on me.
Given the amount of monkey patching I had to do on livereload recently (see lepture/python-livereload#214), I've been wondering about ditching it as a dependency and reimplementing that functionality in staticsite. That would integrate well with an extra empty block in the base template that 'ssite serve' could fill with CSP.
(from a conversation with @DonKult)
It is a great feature, then it works. Sadly it isn't always. Sometimes
it just stops noticing changes. And if it does even manual reload isn't
working anymore. No idea how to debug this. Also, modifying staticsite
itself or features seems to more often than not crash 'ssite serve'
– not really, sadly, the server is still running, it just doesn't serve
anything anymore.
Yes, indeed, and I find it very frustrating. See lepture/python-livereload#176 and lepture/python-livereload#170.
This adds to my itch of ditching livereload and implementing the thing properly.
I also happen to have recently written code to do inotify from python and asyncio reasonably painlessly: https://github.com/himblick/himblick/blob/master/himblib/player.py#L317
livereload was kind of too good to be true, and thanks to it ssite serve
quickly wrote itself, which was great.
It might be that the 'too good to be true' is reaching its limit.
(from a conversation with @DonKult)
The sha256 is for the script-tag livereload inserts. I could just leave
that in for production, but sometimes it would be handy to know if we
are 'build', 'serve'd or perhaps even 'show'n.Well, super-ideally livereload would apply that themself although that
could become complicated really fast on less static sites.
Oh, interesting problem, that. I haven't yet gained CSP as a habit, shame on me.
Given the amount of monkey patching I had to do on livereload recently (see lepture/python-livereload#214), I've been wondering about ditching it as a dependency and reimplementing that functionality in staticsite. That would integrate well with an extra empty block in the base template that 'ssite serve' could fill with CSP.
Meanwhile, exporting the current command to the templates is a much easier thing to do.
======================================================================
FAIL: test_site (tests.test_page_filter.TestPageFilter)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/edg/devel/fork/staticsite/tests/test_page_filter.py", line 22, in test_site
self.assertEqual(select(site, path="blog/*"), [
AssertionError: Lists differ: ['/blog/post2', '/blog/post1'] != ['/blog/post1', '/blog/post2']
First differing element 0:
'/blog/post2'
'/blog/post1'
- ['/blog/post2', '/blog/post1']
+ ['/blog/post1', '/blog/post2']
----------------------------------------------------------------------
Ran 56 tests in 3.283s
FAILED (failures=1)
Test failed: <unittest.runner.TextTestResult run=56 errors=0 failures=1>
error: Test failed: <unittest.runner.TextTestResult run=56 errors=0 failures=1>
https://panzi.github.io/SocialSharePrivacy/ looks nice.
staticsite could have at least a HOWTO on how to enable that for a blog.
Hi,
the API was changed in 3.0 already, but with 3.4 the old APIs (and deprecation warnings I at least haven't seen) were removed.
References:
This diff seems to work for me, but as I have no idea what a good int value for priority
should be (as _end
is not accepted) I am just dropping this here – also, no idea how backward compatible that API is and/or if that is a concern.
diff --git a/staticsite/features/markdown.py b/staticsite/features/markdown.py
--- a/staticsite/features/markdown.py
+++ b/staticsite/features/markdown.py
@@ -120,10 +120,10 @@ class LinkResolver(markdown.treeprocessors.Treeprocessor):
class StaticSiteExtension(markdown.extensions.Extension):
- def extendMarkdown(self, md, md_globals):
+ def extendMarkdown(self, md):
self.link_resolver = LinkResolver(md)
# Insert instance of 'mypattern' before 'references' pattern
- md.treeprocessors.add('staticsite', self.link_resolver, '_end')
+ md.treeprocessors.register(self.link_resolver, 'staticsite', 50)
md.registerExtension(self)
def reset(self):
Lets say I want to include the blog/archive
page in the navbar, how best to set e.g. nav_title for this page given that it is auto-generated?
I can create the page manually, but that produces a) a warning for replacing a page and b) it has only an effect for the page itself, others still use the (not available) metadata of the auto-generated page.
Beside, it is also a bit strange that the archive page has the same title as the page they are an archive for. For taxonomies you can at least control the title (in theory, this seems broken in practice?), but again no dice for other metadata like nav_title.
(I am using/adding my own metadata fields left and right, so having a generic solution would be nice, but I guess that is features territory.)
(from a conversation with @DonKult)
Is there actually a way to prevent staticsite from load say "tags" – so that I may load "tags-ng"?
I've been considering implementing something like a features_exclude
setting, but so far I haven't had a need for it so I procrastinated it.
You give me a good reason to have it.Also, depending on a feature in the deny list should in theory just
ignore the dependency, or possibly disable loading of the depending
feature? In your case, however, you'd like to have tags-ng run instead
of tags, and then series would integrate with tags-ng instead of tags,
so I guess that disabling series if tags is blacklisted wouldn't work
for you.
As a note: This imagined tags-ng does not exist nor do I actually plan
for one. It does indeed poses a set of interesting design possibilities…
In theory a tags-ng would need a way to say "I am like tags" (aka
Provides: tags), but that leads eventually also to all the other dependency
relations and a full-blown resolver…
There could be a possible shortcut here: features define the name they take in the feature database, as such:
FEATURES = {
"tags": TaxonomyFeature,
}
It could be possible to say that if two features register themselves with the same name, the last one wins and the previous one get ignored.
When loading features, one can postpone instantiating the feature classes until both the built-in and the theme features have been loaded, and only then build the dependency graph, do the topological sorting, and instantiate feature classes.
Similar to debian-policy, staticsite currently has a problem if a page has footnotes in multiple blocks – e.g. if the page is an index page of a blog: the [^1] footnotes in each block would get the same id.
By setting the right option in the right way the ids include a block-number to be unique:
MARKDOWN_EXTENSION_CONFIGS = {
'markdown.extensions.extra': {
'markdown.extensions.footnotes': {
'UNIQUE_IDS': True,
},
},
}
Setting options for extensions bundled in the extra extension is a bit tricky, an explanation can be found in upstreams bugtracker.
I guess that should be a builtin default, but it breaks existing links with footnotes, so perhaps it should just be a project specific setting (hint hint)… decisions decisions…
(from a conversation with @DonKult)
If a Features has neither RUN_AFTER nor RUN_BEFORE it doesn't run at all. Same if it has but non of the mentioned features exist. The doc says nothing about this. Perhaps add an implicit _end dependency to everything?
Hi,
installing staticsite in Debian left me with an unresolved runtime dependency on python3-slugify I had not installed and a few more I had just by chance. Looking at the buildlog shows lines like these: I: dh_python3 pydist:191: Cannot find package that provides yaml. Please add package that provides it to Build-Depends or add "yaml python3-yaml" line to debian/py3dist-overrides or add proper dependency to Depends by hand and ignore this info.
It seems like everything is picked up correctly if you would use requires=[ 'unidecode', 'markdown', 'toml', 'PyYAML', 'jinja2', 'python_dateutil', 'livereload', 'python_slugify' ],
in setup.py (slugify added and yaml & dateutil converted to the name given in their egg files in Debian) but I know next to nothing about the python ecosystem to judge if that would really be the correct/best change.
Create a new navbar
feature to provide to the base template data used to build the navbar.
A basic option is that a page could add itself to the navbar of other pages, similarly to how syndication
's add_to
works.
(I think I have a new hobby: Reporting bugs by pointing to the website of the author)
Browsing e.g. to https://www.enricozini.org/blog/2020/ssite/ shows that no link for the parent directory is generated and inspecting the metadata shows that indeed pages do no get parents set although the refdoc documents such a "hierarcny" [sic!].
The 2020 directory listing shows also that something is clearly up with the title for these auto-generated pages as all sub-directories are listed with the same title (of the first not-auto-generated page in the hierarchy)…
When trying to install with
pip install -e .
the install process stops because setup.py
references the old PIL
instead of the modern fork Pillow
.
After fixing this, running ssite serve
from a clean virtualenv also fails, because the required pyinotify
dependency is undeclared in setup.py
...
See e.g. the pdo tag feed from your own site:
<title>: posts with tag pdo</title>
<description>: most recent posts with tag pdo</description>
Given the settings page.meta.site_name
should have been before the : in both. The value is shown in ssite dump_meta
and if used directly in the template its printed just fine, so I guess it is a matter of loading/setting order.
(From a conversation wtih @DonKult)
I have a silly local patch adding it as an additional parameter as globs
sometimes aren't enough, but somehow I think it should be possible to
annotate site_pages(path= somehow that I am passing a regex rather than
a fnmatch which is then translated to a regex.
I can think of two ways:
- if path is a regexp object, use it directly. Export function to the
jinja2 templates that is an alias to re.compile. It could be called
'regex', 're', or 'R'.- add another argument
path_re
which, if present, overridespath
and
is used as a regexp
As I tried to explain I did 2., but I don't like it, so yeah, 1. would
be nice. I would recommend a not too generic/short alias though.
Ok. Let's try and accept a regexp object for path, and export a regex()
function to the templates.
When writing rendered pages and assets to the destination directory during build, if staticsite has a mtime
for the rendered content (like an assed source image mtime, or the most recent date for a page in a syndication), the resulting file mtimes could be set to match it.
This allows a web browser to handle correctly, for example, If-Modified-Since
headers from webbrowser, and to more aggressively skip rewriting pages that didn't change since the last site build.
Hi (again),
"third time's the charm", so instead of bugs I will ask for a feature this time: I think it would be handy if you could have
{% filter markdown %}
*This* is an [example](http://example.org)
{% endfilter %}
in a jinja2 template as that makes it e.g. a lot easier to convert an md page to a j2 page (or back) later on.
It shouldn't be too hard™, a proof-of-concept from a complete python-newbie: DonKult@fcbcbaa
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.