Collection of themes/skins for the Fossil SCM. Public write access, just make an account.

⌈⌋ branch:  Fossil Skins Extra


Check-in [5066f4ec9f]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
SHA1:5066f4ec9f8ad6d3eea94542c0d31de47c19dde0
Date: 2016-09-25 17:48:26
User: mario
Comment:Generates a "common-repo.json" list from specified files in a fossil repository. (Used with a glob param like "/repo.json/REPO/files/*.py" to slice out interesting meta information.)
Tags And Properties
  • branch=trunk inherited from [9f6fc9a491]
  • sym-trunk inherited from [9f6fc9a491]
Context
2016-09-25
17:48
[5066f4ec9f] Leaf: Generates a "common-repo.json" list from specified files in a fossil repository. (Used with a glob param like "/repo.json/REPO/files/*.py" to slice out interesting meta information.) (user: mario, tags: trunk)
2016-09-04
12:37
[7cafda9321] Fixed googlecode links to github raw CDN. (user: mario, tags: trunk)
Changes

Added features/pluginconf-repo.php.

































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
<?php
/**
 * api: php
 * type: handler
 * title: Common.json repo list
 * description: Create json dict of selected fossil repository contents
 * version: 0.1
 * depends: fossil:json
 * doc: http://fossil.include-once.org/streamtuner2/wiki?name=plugin+meta+data
 *      https://pypi.python.org/pypi/pluginconf/
 *
 * Generates a „common-repo“-json list for files from a fossil repository.
 *
 *  · Used for streamtuner2 plugin manager - to refresh module list directly
 *    from repositories.
 *
 *  · Extracts key:value comments as seen above.
 *
 *  · Accepts PATH_INFO specifiers using glob patterns `/reponame/src*dir/*.ext`.
 *
 *  · You'll probably want to hook it beside the actual fossil server, using
 *    using a RewriteRule or ScriptAliasMatch.
 *    e.g.
 *        RewriteRule   ^(/?)repo.json/(.+)$ $1plugins.php/$2
 *
 *  · Each entry carries a faux $type, $dist and $file references, and all
 *    extracted meta fields, no docs. (= The crudest implementation so far.)
 *
 */


// run
$p = opts() and gen($p);



/**
 * Request params
 * [WHITELIST]
 *
 *  · assert alphanumeric repository.fossil name
 *  · limit allowed glob specifiers and file paths
 *
 */
function opts() {
    preg_match(
        "~^
           /(?<repo>[\w-]+)            # fossil basename
           /(?<glob>
              (?:
               [/\w.-]+                # basedir prefix
               [*]?                    
              ){0,3}                   # up to 3 *-glob segments
            )
        $~x",
        $_SERVER["PATH_INFO"], $groups
    );
    return $groups;
}


/**
 * Handler
 * [MAIN]
 *
 *  · invoked with $repo="projectname" and $glob="lib/*.src"
 *  · scans files, converts into repo.json, and outputs that
 *
 */
function gen($kwargs) {
    extract($kwargs);
    header("Content-Type: json/common-repo; x-ver=0");
    exit(
        json_encode(
            common_repo(
                query_files("/fossil.d/$repo.fossil", $glob),
                $repo
            ),
            JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT
        )
    );
}



/**
 * Read glob-specified files from repository
 * [EXEC]
 *
 *  · scans fossil repos for given filespec, e.g. "lib/plugins/*.py"
 *  · returns just first 2K from content
 *
 */
function query_files($R, $glob) {
   $r = [];
   if (!file_exists($R)) {
       return $r;
   }

   // loop through files
   $glob = escapeshellarg(preg_replace("~[^/\w.*-]~", "", $glob));
   $sql = escapeshellarg("
      SELECT  name AS name,  uuid,   SUBSTR(HEX(CONTENT(uuid)),1,2048) AS content
         FROM (SELECT  filename.name, bf.uuid, filename.fnid 
               FROM filename  JOIN mlink ON mlink.fnid=filename.fnid JOIN blob bf ON bf.rid=mlink.fid
                    JOIN event ON event.objid=mlink.mid
               WHERE (mlink.fnid NOT IN (SELECT fnid FROM mlink WHERE fid=0))
                     AND filename.name GLOB $glob
               GROUP BY filename.name
               ORDER BY event.mtime DESC
              )
   ");

   // Just retrieve as CSV list from fossil directly,
   // instead of using PDO handle and `fossil artifact` on each UUID
   $pipe = popen("fossil sqlite -R $R \".mode csv\" $sql", "rb");
   while ($row = fgetcsv($pipe, 0, ",", '"', '"')) {

      // skip emtpy rows
      if (count($row) != 3) {
          continue;
      }
      // add file
      $r[$row[0]] = hex2bin($row[2]);
   }

   return $r;
}


/**
 * Convert attributes into list
 * [TRANSFORM]
 *
 *  · from $fn=>$meta dict to [$pkg, $pkg] list
 *
 */
function common_repo($files, $R) {

    // extend each PMD key:value list
    $repo = meta_extract($files);
    foreach ($repo as $fn => $meta) {
    
        // basename, extension
        $id = strtok(basename($fn), ".");
        $ext = pathinfo($fn, PATHINFO_EXTENSION);
        $dir = basename(dirname($fn));
        
        // add some stub fields
        $meta += [
            "type" => "unknown",
            "api" => "$R",
            "title" => null,
            "description" => null,
            "version" => null,
        ];
        
        // common repo fields carry a `$` sigil
        $repo[$fn] = array_merge(
            [
                "\$name" => $id,                         # package basename
                "\$type" => "x-$ext",                    # e.g. "deb", "rpm" or "x-src"
                "\$dist" => "app/$R/$dir",               # e.g. "trusty/main" or "app:pkg:part"
                "\$file" => "http://fossil.include-once.org/$R/cat/$fn",   # resource locator
            ],
            $meta
        );
    }
    return array_values($repo);
}


/**
 * Extract plugin meta data
 * [REGEX]
 *
 *  · really just looks for scalar key:value lines
 *  · comment/docs are not extracted
 *
 */
function meta_extract($files) {
    foreach ($files as $fn=>$cnt) {
        preg_match_all("~\s*(?:#|//|[*])\s*(\w+):\h*(.+)~m", $cnt, $fields);
        $meta = array_combine($fields[1], $fields[2]);
        $meta = array_change_key_case($meta);
        $files[$fn] = $meta;
    }
    return $files;
}