Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

avocado-python-utils

A collection of handy utilities put together while building Avocado's web infrastructure.

build_file_index.py

This script takes a list of input directories containing web resources (JS, CSS, images, whathaveyou), along with some options, and outputs a versioned directory containing the files, renamed according to their MD5 hashes, along with a JSON index file to use as a map between the original filename and the resulting versioned filename.

Using this script, you can:

  1. Serve versioned resource URLs and cache them infinitely.

  2. Offload static resources to a static resources server (or S3 bucket) by rewriting references on your web frontend to read paths from the JSON index.

This script was heavily inspired by VFL, the mechanism YouTube employs to serve its static resources, which is available on Google Code.

Example output

When the script is run, you'll end up with an output directory containing renamed files, along with a JSON blob.

Static directories, input and output

Versioned tree

JSON index from raw to versioned name

{
"css-out/base_public.css": "//avocado-static.s3.amazonaws.com/css-out/base_public-vfce4345d.css",
"js-out/avo.js": "//avocado-static.s3.amazonaws.com/js-out/avo-v7411f264.js",
"imgs/favicon.ico": "//avocado-static.s3.amazonaws.com/imgs/favicon-v4e15b756.ico",
"imgs/pixel.gif": "//avocado-static.s3.amazonaws.com/imgs/pixel-vdf3e567d.gif",
...
}

NOTE: inside CSS and JS files, references to other resources [eg. /static/foo-bar.png] are also updated to match the versioned filenames found in the JSON blob.

Also, in this case, //avocado-static.s3.amazonaws.com/ is the --outprefix option.

How to

  1. During development, serve static resources from a directory, eg. "/static/", which specifies the Cache-Control: no-cache HTTP header for ease of development.

  2. Inside static JavaScript and CSS files, reference other resources using this prefix, like so:

     /* CSS file located at /static/some-css-file.css */
    .some-class {
    background: url(/static/some-image.png);
    }
    
  3. Add a helper function for templates to call that translates paths relative to /static/ to a "versioned" URL:

     <script src="{{ static_url("some-js-file.js") }}"></script>
    

    In development, this just needs to prepend "/static/" -- but in production, it will need to read the JSON index like so:

     STATIC_FILE_INDEX = os.path.join(WEB_DIR, 'static-index.json')
    _file_index = {}
    def get_static_url(filename):
    if not _file_index:
    try:
    index = open(STATIC_FILE_INDEX, 'r')
    _file_index = json.load(index)
    except:
    logging.exception('Could not read static file index.')
    return _file_index.get(filename)
    
  4. During deployment, run this script, and include static-index.json in the deployed product:

     ./build_file_index.py path/to/static path/to/static-out --indexout path/to/web/static-index.json
    
  5. Optionally, you can feed --outprefix //some-bucket.s3.amazonaws.com/ or whatever arbitrary external host to the script, and rsync the static-out directory the script creates to offload static resources from your primary web host. (Note that since the files themselves are renamed, it is possible to host mutiple versions of your static resources side-by-side, to allow for partial or gradual rollouts, rollbacks, etc. -- in which un-deployed frontends still point at an older versioned filename.)

migrate_mysql.py

This script simply takes a directory of named .sql files and runs them against the database specified by the command-line arguments, and records what has been done in a table in the database itself.

This is very similar to migrations in Rails, but without any Ruby -- just a dead simple way to allow different developers to make database changes while working together in a distributed environment.

If multiple new migrations are detected in the migrations directory, they are performed in order.

./migrate_mysql.py my_database_name \
--migrations_dir ./migrations/ \
--user notroot \
--password p4ssw0rd

The script in this case will search ./migrations/ for files ending in .sql, remove the file extension (considering what remains the migration's unique identifier), and sort the migration identifiers, and attempt to execute them in order.

If a migration fails due to an SQL error, you may need to manually intervene (eg. by deleting a half-created table). The migrator, however, will consider the migration to be "run", and will not attempt to re-run it. If you'd like to re-run a migration, you can pass the following option:

# note that the migration name does not end in .sql
./migrate_mysql.py [options] --rerun 20120412_migration_name

... or just delete the migration with that name in the migrations table.

About

A collection of handy utilities put together while building Avocado's web infrastructure.

Resources

Stars

5 stars

Watchers

7 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

avocado-python-utils

A collection of handy utilities put together while building Avocado's web infrastructure.

build_file_index.py

This script takes a list of input directories containing web resources (JS, CSS, images, whathaveyou), along with some options, and outputs a versioned directory containing the files, renamed according to their MD5 hashes, along with a JSON index file to use as a map between the original filename and the resulting versioned filename.

Using this script, you can:

  1. Serve versioned resource URLs and cache them infinitely.

  2. Offload static resources to a static resources server (or S3 bucket) by rewriting references on your web frontend to read paths from the JSON index.

This script was heavily inspired by VFL, the mechanism YouTube employs to serve its static resources, which is available on Google Code.

Example output

When the script is run, you'll end up with an output directory containing renamed files, along with a JSON blob.

Static directories, input and output

Versioned tree

JSON index from raw to versioned name

{
"css-out/base_public.css": "//avocado-static.s3.amazonaws.com/css-out/base_public-vfce4345d.css",
"js-out/avo.js": "//avocado-static.s3.amazonaws.com/js-out/avo-v7411f264.js",
"imgs/favicon.ico": "//avocado-static.s3.amazonaws.com/imgs/favicon-v4e15b756.ico",
"imgs/pixel.gif": "//avocado-static.s3.amazonaws.com/imgs/pixel-vdf3e567d.gif",
...
}

NOTE: inside CSS and JS files, references to other resources [eg. /static/foo-bar.png] are also updated to match the versioned filenames found in the JSON blob.

Also, in this case, //avocado-static.s3.amazonaws.com/ is the --outprefix option.

How to

  1. During development, serve static resources from a directory, eg. "/static/", which specifies the Cache-Control: no-cache HTTP header for ease of development.

  2. Inside static JavaScript and CSS files, reference other resources using this prefix, like so:

     /* CSS file located at /static/some-css-file.css */
    .some-class {
    background: url(/static/some-image.png);
    }
    
  3. Add a helper function for templates to call that translates paths relative to /static/ to a "versioned" URL:

     <script src="{{ static_url("some-js-file.js") }}"></script>
    

    In development, this just needs to prepend "/static/" -- but in production, it will need to read the JSON index like so:

     STATIC_FILE_INDEX = os.path.join(WEB_DIR, 'static-index.json')
    _file_index = {}
    def get_static_url(filename):
    if not _file_index:
    try:
    index = open(STATIC_FILE_INDEX, 'r')
    _file_index = json.load(index)
    except:
    logging.exception('Could not read static file index.')
    return _file_index.get(filename)
    
  4. During deployment, run this script, and include static-index.json in the deployed product:

     ./build_file_index.py path/to/static path/to/static-out --indexout path/to/web/static-index.json
    
  5. Optionally, you can feed --outprefix //some-bucket.s3.amazonaws.com/ or whatever arbitrary external host to the script, and rsync the static-out directory the script creates to offload static resources from your primary web host. (Note that since the files themselves are renamed, it is possible to host mutiple versions of your static resources side-by-side, to allow for partial or gradual rollouts, rollbacks, etc. -- in which un-deployed frontends still point at an older versioned filename.)

migrate_mysql.py

This script simply takes a directory of named .sql files and runs them against the database specified by the command-line arguments, and records what has been done in a table in the database itself.

This is very similar to migrations in Rails, but without any Ruby -- just a dead simple way to allow different developers to make database changes while working together in a distributed environment.

If multiple new migrations are detected in the migrations directory, they are performed in order.

./migrate_mysql.py my_database_name \
--migrations_dir ./migrations/ \
--user notroot \
--password p4ssw0rd

The script in this case will search ./migrations/ for files ending in .sql, remove the file extension (considering what remains the migration's unique identifier), and sort the migration identifiers, and attempt to execute them in order.

If a migration fails due to an SQL error, you may need to manually intervene (eg. by deleting a half-created table). The migrator, however, will consider the migration to be "run", and will not attempt to re-run it. If you'd like to re-run a migration, you can pass the following option:

# note that the migration name does not end in .sql
./migrate_mysql.py [options] --rerun 20120412_migration_name

... or just delete the migration with that name in the migrations table.

About

A collection of handy utilities put together while building Avocado's web infrastructure.

Resources

Stars

5 stars

Watchers

7 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

avocado-python-utils

A collection of handy utilities put together while building Avocado's web infrastructure.

build_file_index.py

This script takes a list of input directories containing web resources (JS, CSS, images, whathaveyou), along with some options, and outputs a versioned directory containing the files, renamed according to their MD5 hashes, along with a JSON index file to use as a map between the original filename and the resulting versioned filename.

Using this script, you can:

  1. Serve versioned resource URLs and cache them infinitely.

  2. Offload static resources to a static resources server (or S3 bucket) by rewriting references on your web frontend to read paths from the JSON index.

This script was heavily inspired by VFL, the mechanism YouTube employs to serve its static resources, which is available on Google Code.

Example output

When the script is run, you'll end up with an output directory containing renamed files, along with a JSON blob.

Static directories, input and output

Versioned tree

JSON index from raw to versioned name

{
"css-out/base_public.css": "//avocado-static.s3.amazonaws.com/css-out/base_public-vfce4345d.css",
"js-out/avo.js": "//avocado-static.s3.amazonaws.com/js-out/avo-v7411f264.js",
"imgs/favicon.ico": "//avocado-static.s3.amazonaws.com/imgs/favicon-v4e15b756.ico",
"imgs/pixel.gif": "//avocado-static.s3.amazonaws.com/imgs/pixel-vdf3e567d.gif",
...
}

NOTE: inside CSS and JS files, references to other resources [eg. /static/foo-bar.png] are also updated to match the versioned filenames found in the JSON blob.

Also, in this case, //avocado-static.s3.amazonaws.com/ is the --outprefix option.

How to

  1. During development, serve static resources from a directory, eg. "/static/", which specifies the Cache-Control: no-cache HTTP header for ease of development.

  2. Inside static JavaScript and CSS files, reference other resources using this prefix, like so:

     /* CSS file located at /static/some-css-file.css */
    .some-class {
    background: url(/static/some-image.png);
    }
    
  3. Add a helper function for templates to call that translates paths relative to /static/ to a "versioned" URL:

     <script src="{{ static_url("some-js-file.js") }}"></script>
    

    In development, this just needs to prepend "/static/" -- but in production, it will need to read the JSON index like so:

     STATIC_FILE_INDEX = os.path.join(WEB_DIR, 'static-index.json')
    _file_index = {}
    def get_static_url(filename):
    if not _file_index:
    try:
    index = open(STATIC_FILE_INDEX, 'r')
    _file_index = json.load(index)
    except:
    logging.exception('Could not read static file index.')
    return _file_index.get(filename)
    
  4. During deployment, run this script, and include static-index.json in the deployed product:

     ./build_file_index.py path/to/static path/to/static-out --indexout path/to/web/static-index.json
    
  5. Optionally, you can feed --outprefix //some-bucket.s3.amazonaws.com/ or whatever arbitrary external host to the script, and rsync the static-out directory the script creates to offload static resources from your primary web host. (Note that since the files themselves are renamed, it is possible to host mutiple versions of your static resources side-by-side, to allow for partial or gradual rollouts, rollbacks, etc. -- in which un-deployed frontends still point at an older versioned filename.)

migrate_mysql.py

This script simply takes a directory of named .sql files and runs them against the database specified by the command-line arguments, and records what has been done in a table in the database itself.

This is very similar to migrations in Rails, but without any Ruby -- just a dead simple way to allow different developers to make database changes while working together in a distributed environment.

If multiple new migrations are detected in the migrations directory, they are performed in order.

./migrate_mysql.py my_database_name \
--migrations_dir ./migrations/ \
--user notroot \
--password p4ssw0rd

The script in this case will search ./migrations/ for files ending in .sql, remove the file extension (considering what remains the migration's unique identifier), and sort the migration identifiers, and attempt to execute them in order.

If a migration fails due to an SQL error, you may need to manually intervene (eg. by deleting a half-created table). The migrator, however, will consider the migration to be "run", and will not attempt to re-run it. If you'd like to re-run a migration, you can pass the following option:

# note that the migration name does not end in .sql
./migrate_mysql.py [options] --rerun 20120412_migration_name

... or just delete the migration with that name in the migrations table.

About

A collection of handy utilities put together while building Avocado's web infrastructure.

Resources

Stars

5 stars

Watchers

7 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

avocado-python-utils

A collection of handy utilities put together while building Avocado's web infrastructure.

build_file_index.py

This script takes a list of input directories containing web resources (JS, CSS, images, whathaveyou), along with some options, and outputs a versioned directory containing the files, renamed according to their MD5 hashes, along with a JSON index file to use as a map between the original filename and the resulting versioned filename.

Using this script, you can:

  1. Serve versioned resource URLs and cache them infinitely.

  2. Offload static resources to a static resources server (or S3 bucket) by rewriting references on your web frontend to read paths from the JSON index.

This script was heavily inspired by VFL, the mechanism YouTube employs to serve its static resources, which is available on Google Code.

Example output

When the script is run, you'll end up with an output directory containing renamed files, along with a JSON blob.

Static directories, input and output

Versioned tree

JSON index from raw to versioned name

{
"css-out/base_public.css": "//avocado-static.s3.amazonaws.com/css-out/base_public-vfce4345d.css",
"js-out/avo.js": "//avocado-static.s3.amazonaws.com/js-out/avo-v7411f264.js",
"imgs/favicon.ico": "//avocado-static.s3.amazonaws.com/imgs/favicon-v4e15b756.ico",
"imgs/pixel.gif": "//avocado-static.s3.amazonaws.com/imgs/pixel-vdf3e567d.gif",
...
}

NOTE: inside CSS and JS files, references to other resources [eg. /static/foo-bar.png] are also updated to match the versioned filenames found in the JSON blob.

Also, in this case, //avocado-static.s3.amazonaws.com/ is the --outprefix option.

How to

  1. During development, serve static resources from a directory, eg. "/static/", which specifies the Cache-Control: no-cache HTTP header for ease of development.

  2. Inside static JavaScript and CSS files, reference other resources using this prefix, like so:

     /* CSS file located at /static/some-css-file.css */
    .some-class {
    background: url(/static/some-image.png);
    }
    
  3. Add a helper function for templates to call that translates paths relative to /static/ to a "versioned" URL:

     <script src="{{ static_url("some-js-file.js") }}"></script>
    

    In development, this just needs to prepend "/static/" -- but in production, it will need to read the JSON index like so:

     STATIC_FILE_INDEX = os.path.join(WEB_DIR, 'static-index.json')
    _file_index = {}
    def get_static_url(filename):
    if not _file_index:
    try:
    index = open(STATIC_FILE_INDEX, 'r')
    _file_index = json.load(index)
    except:
    logging.exception('Could not read static file index.')
    return _file_index.get(filename)
    
  4. During deployment, run this script, and include static-index.json in the deployed product:

     ./build_file_index.py path/to/static path/to/static-out --indexout path/to/web/static-index.json
    
  5. Optionally, you can feed --outprefix //some-bucket.s3.amazonaws.com/ or whatever arbitrary external host to the script, and rsync the static-out directory the script creates to offload static resources from your primary web host. (Note that since the files themselves are renamed, it is possible to host mutiple versions of your static resources side-by-side, to allow for partial or gradual rollouts, rollbacks, etc. -- in which un-deployed frontends still point at an older versioned filename.)

migrate_mysql.py

This script simply takes a directory of named .sql files and runs them against the database specified by the command-line arguments, and records what has been done in a table in the database itself.

This is very similar to migrations in Rails, but without any Ruby -- just a dead simple way to allow different developers to make database changes while working together in a distributed environment.

If multiple new migrations are detected in the migrations directory, they are performed in order.

./migrate_mysql.py my_database_name \
--migrations_dir ./migrations/ \
--user notroot \
--password p4ssw0rd

The script in this case will search ./migrations/ for files ending in .sql, remove the file extension (considering what remains the migration's unique identifier), and sort the migration identifiers, and attempt to execute them in order.

If a migration fails due to an SQL error, you may need to manually intervene (eg. by deleting a half-created table). The migrator, however, will consider the migration to be "run", and will not attempt to re-run it. If you'd like to re-run a migration, you can pass the following option:

# note that the migration name does not end in .sql
./migrate_mysql.py [options] --rerun 20120412_migration_name

... or just delete the migration with that name in the migrations table.

About

A collection of handy utilities put together while building Avocado's web infrastructure.

Resources

Stars

5 stars

Watchers

7 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

avocado-python-utils

A collection of handy utilities put together while building Avocado's web infrastructure.

build_file_index.py

This script takes a list of input directories containing web resources (JS, CSS, images, whathaveyou), along with some options, and outputs a versioned directory containing the files, renamed according to their MD5 hashes, along with a JSON index file to use as a map between the original filename and the resulting versioned filename.

Using this script, you can:

  1. Serve versioned resource URLs and cache them infinitely.

  2. Offload static resources to a static resources server (or S3 bucket) by rewriting references on your web frontend to read paths from the JSON index.

This script was heavily inspired by VFL, the mechanism YouTube employs to serve its static resources, which is available on Google Code.

Example output

When the script is run, you'll end up with an output directory containing renamed files, along with a JSON blob.

Static directories, input and output

Versioned tree

JSON index from raw to versioned name

{
"css-out/base_public.css": "//avocado-static.s3.amazonaws.com/css-out/base_public-vfce4345d.css",
"js-out/avo.js": "//avocado-static.s3.amazonaws.com/js-out/avo-v7411f264.js",
"imgs/favicon.ico": "//avocado-static.s3.amazonaws.com/imgs/favicon-v4e15b756.ico",
"imgs/pixel.gif": "//avocado-static.s3.amazonaws.com/imgs/pixel-vdf3e567d.gif",
...
}

NOTE: inside CSS and JS files, references to other resources [eg. /static/foo-bar.png] are also updated to match the versioned filenames found in the JSON blob.

Also, in this case, //avocado-static.s3.amazonaws.com/ is the --outprefix option.

How to

  1. During development, serve static resources from a directory, eg. "/static/", which specifies the Cache-Control: no-cache HTTP header for ease of development.

  2. Inside static JavaScript and CSS files, reference other resources using this prefix, like so:

     /* CSS file located at /static/some-css-file.css */
    .some-class {
    background: url(/static/some-image.png);
    }
    
  3. Add a helper function for templates to call that translates paths relative to /static/ to a "versioned" URL:

     <script src="{{ static_url("some-js-file.js") }}"></script>
    

    In development, this just needs to prepend "/static/" -- but in production, it will need to read the JSON index like so:

     STATIC_FILE_INDEX = os.path.join(WEB_DIR, 'static-index.json')
    _file_index = {}
    def get_static_url(filename):
    if not _file_index:
    try:
    index = open(STATIC_FILE_INDEX, 'r')
    _file_index = json.load(index)
    except:
    logging.exception('Could not read static file index.')
    return _file_index.get(filename)
    
  4. During deployment, run this script, and include static-index.json in the deployed product:

     ./build_file_index.py path/to/static path/to/static-out --indexout path/to/web/static-index.json
    
  5. Optionally, you can feed --outprefix //some-bucket.s3.amazonaws.com/ or whatever arbitrary external host to the script, and rsync the static-out directory the script creates to offload static resources from your primary web host. (Note that since the files themselves are renamed, it is possible to host mutiple versions of your static resources side-by-side, to allow for partial or gradual rollouts, rollbacks, etc. -- in which un-deployed frontends still point at an older versioned filename.)

migrate_mysql.py

This script simply takes a directory of named .sql files and runs them against the database specified by the command-line arguments, and records what has been done in a table in the database itself.

This is very similar to migrations in Rails, but without any Ruby -- just a dead simple way to allow different developers to make database changes while working together in a distributed environment.

If multiple new migrations are detected in the migrations directory, they are performed in order.

./migrate_mysql.py my_database_name \
--migrations_dir ./migrations/ \
--user notroot \
--password p4ssw0rd

The script in this case will search ./migrations/ for files ending in .sql, remove the file extension (considering what remains the migration's unique identifier), and sort the migration identifiers, and attempt to execute them in order.

If a migration fails due to an SQL error, you may need to manually intervene (eg. by deleting a half-created table). The migrator, however, will consider the migration to be "run", and will not attempt to re-run it. If you'd like to re-run a migration, you can pass the following option:

# note that the migration name does not end in .sql
./migrate_mysql.py [options] --rerun 20120412_migration_name

... or just delete the migration with that name in the migrations table.

About

A collection of handy utilities put together while building Avocado's web infrastructure.

Resources

Stars

5 stars

Watchers

7 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

avocado-python-utils

A collection of handy utilities put together while building Avocado's web infrastructure.

build_file_index.py

This script takes a list of input directories containing web resources (JS, CSS, images, whathaveyou), along with some options, and outputs a versioned directory containing the files, renamed according to their MD5 hashes, along with a JSON index file to use as a map between the original filename and the resulting versioned filename.

Using this script, you can:

  1. Serve versioned resource URLs and cache them infinitely.

  2. Offload static resources to a static resources server (or S3 bucket) by rewriting references on your web frontend to read paths from the JSON index.

This script was heavily inspired by VFL, the mechanism YouTube employs to serve its static resources, which is available on Google Code.

Example output

When the script is run, you'll end up with an output directory containing renamed files, along with a JSON blob.

Static directories, input and output

Versioned tree

JSON index from raw to versioned name

{
"css-out/base_public.css": "//avocado-static.s3.amazonaws.com/css-out/base_public-vfce4345d.css",
"js-out/avo.js": "//avocado-static.s3.amazonaws.com/js-out/avo-v7411f264.js",
"imgs/favicon.ico": "//avocado-static.s3.amazonaws.com/imgs/favicon-v4e15b756.ico",
"imgs/pixel.gif": "//avocado-static.s3.amazonaws.com/imgs/pixel-vdf3e567d.gif",
...
}

NOTE: inside CSS and JS files, references to other resources [eg. /static/foo-bar.png] are also updated to match the versioned filenames found in the JSON blob.

Also, in this case, //avocado-static.s3.amazonaws.com/ is the --outprefix option.

How to

  1. During development, serve static resources from a directory, eg. "/static/", which specifies the Cache-Control: no-cache HTTP header for ease of development.

  2. Inside static JavaScript and CSS files, reference other resources using this prefix, like so:

     /* CSS file located at /static/some-css-file.css */
    .some-class {
    background: url(/static/some-image.png);
    }
    
  3. Add a helper function for templates to call that translates paths relative to /static/ to a "versioned" URL:

     <script src="{{ static_url("some-js-file.js") }}"></script>
    

    In development, this just needs to prepend "/static/" -- but in production, it will need to read the JSON index like so:

     STATIC_FILE_INDEX = os.path.join(WEB_DIR, 'static-index.json')
    _file_index = {}
    def get_static_url(filename):
    if not _file_index:
    try:
    index = open(STATIC_FILE_INDEX, 'r')
    _file_index = json.load(index)
    except:
    logging.exception('Could not read static file index.')
    return _file_index.get(filename)
    
  4. During deployment, run this script, and include static-index.json in the deployed product:

     ./build_file_index.py path/to/static path/to/static-out --indexout path/to/web/static-index.json
    
  5. Optionally, you can feed --outprefix //some-bucket.s3.amazonaws.com/ or whatever arbitrary external host to the script, and rsync the static-out directory the script creates to offload static resources from your primary web host. (Note that since the files themselves are renamed, it is possible to host mutiple versions of your static resources side-by-side, to allow for partial or gradual rollouts, rollbacks, etc. -- in which un-deployed frontends still point at an older versioned filename.)

migrate_mysql.py

This script simply takes a directory of named .sql files and runs them against the database specified by the command-line arguments, and records what has been done in a table in the database itself.

This is very similar to migrations in Rails, but without any Ruby -- just a dead simple way to allow different developers to make database changes while working together in a distributed environment.

If multiple new migrations are detected in the migrations directory, they are performed in order.

./migrate_mysql.py my_database_name \
--migrations_dir ./migrations/ \
--user notroot \
--password p4ssw0rd

The script in this case will search ./migrations/ for files ending in .sql, remove the file extension (considering what remains the migration's unique identifier), and sort the migration identifiers, and attempt to execute them in order.

If a migration fails due to an SQL error, you may need to manually intervene (eg. by deleting a half-created table). The migrator, however, will consider the migration to be "run", and will not attempt to re-run it. If you'd like to re-run a migration, you can pass the following option:

# note that the migration name does not end in .sql
./migrate_mysql.py [options] --rerun 20120412_migration_name

... or just delete the migration with that name in the migrations table.

About

A collection of handy utilities put together while building Avocado's web infrastructure.

Resources

Stars

5 stars

Watchers

7 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

avocado-python-utils

A collection of handy utilities put together while building Avocado's web infrastructure.

build_file_index.py

This script takes a list of input directories containing web resources (JS, CSS, images, whathaveyou), along with some options, and outputs a versioned directory containing the files, renamed according to their MD5 hashes, along with a JSON index file to use as a map between the original filename and the resulting versioned filename.

Using this script, you can:

  1. Serve versioned resource URLs and cache them infinitely.

  2. Offload static resources to a static resources server (or S3 bucket) by rewriting references on your web frontend to read paths from the JSON index.

This script was heavily inspired by VFL, the mechanism YouTube employs to serve its static resources, which is available on Google Code.

Example output

When the script is run, you'll end up with an output directory containing renamed files, along with a JSON blob.

Static directories, input and output

Versioned tree

JSON index from raw to versioned name

{
"css-out/base_public.css": "//avocado-static.s3.amazonaws.com/css-out/base_public-vfce4345d.css",
"js-out/avo.js": "//avocado-static.s3.amazonaws.com/js-out/avo-v7411f264.js",
"imgs/favicon.ico": "//avocado-static.s3.amazonaws.com/imgs/favicon-v4e15b756.ico",
"imgs/pixel.gif": "//avocado-static.s3.amazonaws.com/imgs/pixel-vdf3e567d.gif",
...
}

NOTE: inside CSS and JS files, references to other resources [eg. /static/foo-bar.png] are also updated to match the versioned filenames found in the JSON blob.

Also, in this case, //avocado-static.s3.amazonaws.com/ is the --outprefix option.

How to

  1. During development, serve static resources from a directory, eg. "/static/", which specifies the Cache-Control: no-cache HTTP header for ease of development.

  2. Inside static JavaScript and CSS files, reference other resources using this prefix, like so:

     /* CSS file located at /static/some-css-file.css */
    .some-class {
    background: url(/static/some-image.png);
    }
    
  3. Add a helper function for templates to call that translates paths relative to /static/ to a "versioned" URL:

     <script src="{{ static_url("some-js-file.js") }}"></script>
    

    In development, this just needs to prepend "/static/" -- but in production, it will need to read the JSON index like so:

     STATIC_FILE_INDEX = os.path.join(WEB_DIR, 'static-index.json')
    _file_index = {}
    def get_static_url(filename):
    if not _file_index:
    try:
    index = open(STATIC_FILE_INDEX, 'r')
    _file_index = json.load(index)
    except:
    logging.exception('Could not read static file index.')
    return _file_index.get(filename)
    
  4. During deployment, run this script, and include static-index.json in the deployed product:

     ./build_file_index.py path/to/static path/to/static-out --indexout path/to/web/static-index.json
    
  5. Optionally, you can feed --outprefix //some-bucket.s3.amazonaws.com/ or whatever arbitrary external host to the script, and rsync the static-out directory the script creates to offload static resources from your primary web host. (Note that since the files themselves are renamed, it is possible to host mutiple versions of your static resources side-by-side, to allow for partial or gradual rollouts, rollbacks, etc. -- in which un-deployed frontends still point at an older versioned filename.)

migrate_mysql.py

This script simply takes a directory of named .sql files and runs them against the database specified by the command-line arguments, and records what has been done in a table in the database itself.

This is very similar to migrations in Rails, but without any Ruby -- just a dead simple way to allow different developers to make database changes while working together in a distributed environment.

If multiple new migrations are detected in the migrations directory, they are performed in order.

./migrate_mysql.py my_database_name \
--migrations_dir ./migrations/ \
--user notroot \
--password p4ssw0rd

The script in this case will search ./migrations/ for files ending in .sql, remove the file extension (considering what remains the migration's unique identifier), and sort the migration identifiers, and attempt to execute them in order.

If a migration fails due to an SQL error, you may need to manually intervene (eg. by deleting a half-created table). The migrator, however, will consider the migration to be "run", and will not attempt to re-run it. If you'd like to re-run a migration, you can pass the following option:

# note that the migration name does not end in .sql
./migrate_mysql.py [options] --rerun 20120412_migration_name

... or just delete the migration with that name in the migrations table.

About

A collection of handy utilities put together while building Avocado's web infrastructure.

Resources

Stars

5 stars

Watchers

7 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

avocado-python-utils

A collection of handy utilities put together while building Avocado's web infrastructure.

build_file_index.py

This script takes a list of input directories containing web resources (JS, CSS, images, whathaveyou), along with some options, and outputs a versioned directory containing the files, renamed according to their MD5 hashes, along with a JSON index file to use as a map between the original filename and the resulting versioned filename.

Using this script, you can:

  1. Serve versioned resource URLs and cache them infinitely.

  2. Offload static resources to a static resources server (or S3 bucket) by rewriting references on your web frontend to read paths from the JSON index.

This script was heavily inspired by VFL, the mechanism YouTube employs to serve its static resources, which is available on Google Code.

Example output

When the script is run, you'll end up with an output directory containing renamed files, along with a JSON blob.

Static directories, input and output

Versioned tree

JSON index from raw to versioned name

{
"css-out/base_public.css": "//avocado-static.s3.amazonaws.com/css-out/base_public-vfce4345d.css",
"js-out/avo.js": "//avocado-static.s3.amazonaws.com/js-out/avo-v7411f264.js",
"imgs/favicon.ico": "//avocado-static.s3.amazonaws.com/imgs/favicon-v4e15b756.ico",
"imgs/pixel.gif": "//avocado-static.s3.amazonaws.com/imgs/pixel-vdf3e567d.gif",
...
}

NOTE: inside CSS and JS files, references to other resources [eg. /static/foo-bar.png] are also updated to match the versioned filenames found in the JSON blob.

Also, in this case, //avocado-static.s3.amazonaws.com/ is the --outprefix option.

How to

  1. During development, serve static resources from a directory, eg. "/static/", which specifies the Cache-Control: no-cache HTTP header for ease of development.

  2. Inside static JavaScript and CSS files, reference other resources using this prefix, like so:

     /* CSS file located at /static/some-css-file.css */
    .some-class {
    background: url(/static/some-image.png);
    }
    
  3. Add a helper function for templates to call that translates paths relative to /static/ to a "versioned" URL:

     <script src="{{ static_url("some-js-file.js") }}"></script>
    

    In development, this just needs to prepend "/static/" -- but in production, it will need to read the JSON index like so:

     STATIC_FILE_INDEX = os.path.join(WEB_DIR, 'static-index.json')
    _file_index = {}
    def get_static_url(filename):
    if not _file_index:
    try:
    index = open(STATIC_FILE_INDEX, 'r')
    _file_index = json.load(index)
    except:
    logging.exception('Could not read static file index.')
    return _file_index.get(filename)
    
  4. During deployment, run this script, and include static-index.json in the deployed product:

     ./build_file_index.py path/to/static path/to/static-out --indexout path/to/web/static-index.json
    
  5. Optionally, you can feed --outprefix //some-bucket.s3.amazonaws.com/ or whatever arbitrary external host to the script, and rsync the static-out directory the script creates to offload static resources from your primary web host. (Note that since the files themselves are renamed, it is possible to host mutiple versions of your static resources side-by-side, to allow for partial or gradual rollouts, rollbacks, etc. -- in which un-deployed frontends still point at an older versioned filename.)

migrate_mysql.py

This script simply takes a directory of named .sql files and runs them against the database specified by the command-line arguments, and records what has been done in a table in the database itself.

This is very similar to migrations in Rails, but without any Ruby -- just a dead simple way to allow different developers to make database changes while working together in a distributed environment.

If multiple new migrations are detected in the migrations directory, they are performed in order.

./migrate_mysql.py my_database_name \
--migrations_dir ./migrations/ \
--user notroot \
--password p4ssw0rd

The script in this case will search ./migrations/ for files ending in .sql, remove the file extension (considering what remains the migration's unique identifier), and sort the migration identifiers, and attempt to execute them in order.

If a migration fails due to an SQL error, you may need to manually intervene (eg. by deleting a half-created table). The migrator, however, will consider the migration to be "run", and will not attempt to re-run it. If you'd like to re-run a migration, you can pass the following option:

# note that the migration name does not end in .sql
./migrate_mysql.py [options] --rerun 20120412_migration_name

... or just delete the migration with that name in the migrations table.

About

A collection of handy utilities put together while building Avocado's web infrastructure.

Resources

Stars

5 stars

Watchers

7 watching

Forks

Releases

Packages

Contributors

Languages